feat: Add PENDING_DELETE filter to all paginated queries and implement accordion-style drawer navigation

- Add sync_status != 'PENDING_DELETE' condition to all PagingSource queries in LinkDao (getAllLinksPaged, searchLinks, searchLinksFullText, getLinksByTag, getLinksByMultipleTags, getLinksInCollectionPaged, getDeadLinks, getPinnedLinksPaged)
- Update LinkRepositoryImpl to exclude PENDING_DELETE items in dynamic query builder and multi-tag filtering
- Replace independent drawer accordion states
This commit is contained in:
Bruno Charest 2026-02-12 11:24:28 -05:00
parent b9efb14b8d
commit e742502bdc
5 changed files with 26 additions and 22 deletions

View File

@ -25,7 +25,7 @@ interface LinkDao {
@Query("SELECT * FROM links ORDER BY is_pinned DESC, created_at DESC") @Query("SELECT * FROM links ORDER BY is_pinned DESC, created_at DESC")
fun getAllLinks(): Flow<List<LinkEntity>> fun getAllLinks(): Flow<List<LinkEntity>>
@Query("SELECT * FROM links ORDER BY is_pinned DESC, created_at DESC") @Query("SELECT * FROM links WHERE sync_status != 'PENDING_DELETE' ORDER BY is_pinned DESC, created_at DESC")
fun getAllLinksPaged(): PagingSource<Int, LinkEntity> fun getAllLinksPaged(): PagingSource<Int, LinkEntity>
@Query("SELECT * FROM links WHERE id = :id") @Query("SELECT * FROM links WHERE id = :id")
@ -41,9 +41,10 @@ interface LinkDao {
@Query(""" @Query("""
SELECT * FROM links SELECT * FROM links
WHERE title LIKE '%' || :query || '%' WHERE sync_status != 'PENDING_DELETE'
AND (title LIKE '%' || :query || '%'
OR description LIKE '%' || :query || '%' OR description LIKE '%' || :query || '%'
OR url LIKE '%' || :query || '%' OR url LIKE '%' || :query || '%')
ORDER BY is_pinned DESC, created_at DESC ORDER BY is_pinned DESC, created_at DESC
""") """)
fun searchLinks(query: String): PagingSource<Int, LinkEntity> fun searchLinks(query: String): PagingSource<Int, LinkEntity>
@ -52,6 +53,7 @@ interface LinkDao {
SELECT links.* FROM links SELECT links.* FROM links
INNER JOIN links_fts ON links.id = links_fts.rowid INNER JOIN links_fts ON links.id = links_fts.rowid
WHERE links_fts MATCH :query WHERE links_fts MATCH :query
AND links.sync_status != 'PENDING_DELETE'
ORDER BY links.is_pinned DESC, links.created_at DESC ORDER BY links.is_pinned DESC, links.created_at DESC
""") """)
fun searchLinksFullText(query: String): PagingSource<Int, LinkEntity> fun searchLinksFullText(query: String): PagingSource<Int, LinkEntity>
@ -59,6 +61,7 @@ interface LinkDao {
@Query(""" @Query("""
SELECT * FROM links SELECT * FROM links
WHERE tags LIKE '%' || :tag || '%' WHERE tags LIKE '%' || :tag || '%'
AND sync_status != 'PENDING_DELETE'
ORDER BY is_pinned DESC, created_at DESC ORDER BY is_pinned DESC, created_at DESC
""") """)
fun getLinksByTag(tag: String): PagingSource<Int, LinkEntity> fun getLinksByTag(tag: String): PagingSource<Int, LinkEntity>
@ -66,6 +69,7 @@ interface LinkDao {
@Query(""" @Query("""
SELECT * FROM links SELECT * FROM links
WHERE tags LIKE '%' || :tag1 || '%' AND tags LIKE '%' || :tag2 || '%' WHERE tags LIKE '%' || :tag1 || '%' AND tags LIKE '%' || :tag2 || '%'
AND sync_status != 'PENDING_DELETE'
ORDER BY is_pinned DESC, created_at DESC ORDER BY is_pinned DESC, created_at DESC
""") """)
fun getLinksByMultipleTags(tag1: String, tag2: String): PagingSource<Int, LinkEntity> fun getLinksByMultipleTags(tag1: String, tag2: String): PagingSource<Int, LinkEntity>
@ -80,6 +84,7 @@ interface LinkDao {
SELECT links.* FROM links SELECT links.* FROM links
INNER JOIN collection_links ON links.id = collection_links.link_id INNER JOIN collection_links ON links.id = collection_links.link_id
WHERE collection_links.collection_id = :collectionId WHERE collection_links.collection_id = :collectionId
AND links.sync_status != 'PENDING_DELETE'
ORDER BY links.is_pinned DESC, collection_links.added_at DESC ORDER BY links.is_pinned DESC, collection_links.added_at DESC
""") """)
fun getLinksInCollectionPaged(collectionId: Long): PagingSource<Int, LinkEntity> fun getLinksInCollectionPaged(collectionId: Long): PagingSource<Int, LinkEntity>
@ -103,6 +108,7 @@ interface LinkDao {
@Query(""" @Query("""
SELECT * FROM links SELECT * FROM links
WHERE link_check_status = 'BROKEN' WHERE link_check_status = 'BROKEN'
AND sync_status != 'PENDING_DELETE'
ORDER BY last_health_check DESC ORDER BY last_health_check DESC
""") """)
fun getDeadLinks(): PagingSource<Int, LinkEntity> fun getDeadLinks(): PagingSource<Int, LinkEntity>
@ -152,7 +158,7 @@ interface LinkDao {
@Query("SELECT * FROM links WHERE is_pinned = 1 ORDER BY created_at DESC") @Query("SELECT * FROM links WHERE is_pinned = 1 ORDER BY created_at DESC")
fun getPinnedLinks(): Flow<List<LinkEntity>> fun getPinnedLinks(): Flow<List<LinkEntity>>
@Query("SELECT * FROM links WHERE is_pinned = 1 ORDER BY created_at DESC") @Query("SELECT * FROM links WHERE is_pinned = 1 AND sync_status != 'PENDING_DELETE' ORDER BY created_at DESC")
fun getPinnedLinksPaged(): PagingSource<Int, LinkEntity> fun getPinnedLinksPaged(): PagingSource<Int, LinkEntity>
// ====== Sync ====== // ====== Sync ======

View File

@ -79,7 +79,7 @@ constructor(
} else { } else {
val whereClause = tags.joinToString(" AND ") { "tags LIKE ?" } val whereClause = tags.joinToString(" AND ") { "tags LIKE ?" }
val sql = val sql =
"SELECT * FROM links WHERE $whereClause ORDER BY is_pinned DESC, created_at DESC" "SELECT * FROM links WHERE sync_status != 'PENDING_DELETE' AND $whereClause ORDER BY is_pinned DESC, created_at DESC"
val args: Array<Any> = tags.map { "%\"$it\"%" }.toTypedArray() val args: Array<Any> = tags.map { "%\"$it\"%" }.toTypedArray()
linkDao.getLinksByTags(SimpleSQLiteQuery(sql, args)) linkDao.getLinksByTags(SimpleSQLiteQuery(sql, args))
} }
@ -420,7 +420,7 @@ constructor(
// ====== Helpers ====== // ====== Helpers ======
private fun buildFilteredQuery(filter: com.shaarit.domain.model.BookmarkFilter): PagingSource<Int, LinkEntity> { private fun buildFilteredQuery(filter: com.shaarit.domain.model.BookmarkFilter): PagingSource<Int, LinkEntity> {
val conditions = mutableListOf<String>() val conditions = mutableListOf<String>("sync_status != 'PENDING_DELETE'")
val args = mutableListOf<Any>() val args = mutableListOf<Any>()
// Time-based filters // Time-based filters

View File

@ -318,10 +318,8 @@ fun FeedScreen(
val reminderViewModel: com.shaarit.presentation.reminders.ReminderViewModel = hiltViewModel() val reminderViewModel: com.shaarit.presentation.reminders.ReminderViewModel = hiltViewModel()
val linkIdsWithReminders by reminderViewModel.linkIdsWithReminders.collectAsState() val linkIdsWithReminders by reminderViewModel.linkIdsWithReminders.collectAsState()
// États des accordéons du drawer // États des accordéons du drawer (accordion: un seul ouvert à la fois)
var mainMenuExpanded by remember { mutableStateOf(true) } var expandedSection by remember { mutableStateOf("main") }
var collectionsExpanded by remember { mutableStateOf(true) }
var tagsExpanded by remember { mutableStateOf(true) }
// Set initial tag filter // Set initial tag filter
LaunchedEffect(initialTagFilter) { viewModel.setInitialTagFilter(initialTagFilter) } LaunchedEffect(initialTagFilter) { viewModel.setInitialTagFilter(initialTagFilter) }
@ -404,8 +402,8 @@ fun FeedScreen(
// Navigation principale - Accordéon // Navigation principale - Accordéon
AccordionSection( AccordionSection(
title = "MENU PRINCIPAL", title = "MENU PRINCIPAL",
expanded = mainMenuExpanded, expanded = expandedSection == "main",
onToggle = { mainMenuExpanded = !mainMenuExpanded }, onToggle = { expandedSection = if (expandedSection == "main") "" else "main" },
modifier = Modifier.padding(top = 8.dp) modifier = Modifier.padding(top = 8.dp)
) { ) {
Column( Column(
@ -493,8 +491,8 @@ fun FeedScreen(
// Types de contenu - Accordéon // Types de contenu - Accordéon
AccordionSection( AccordionSection(
title = "TYPES DE CONTENU", title = "TYPES DE CONTENU",
expanded = true, expanded = expandedSection == "content",
onToggle = { }, onToggle = { expandedSection = if (expandedSection == "content") "" else "content" },
modifier = Modifier.padding(vertical = 4.dp) modifier = Modifier.padding(vertical = 4.dp)
) { ) {
Column( Column(
@ -633,8 +631,8 @@ fun FeedScreen(
// Collections rapides - Accordéon // Collections rapides - Accordéon
AccordionSection( AccordionSection(
title = "COLLECTIONS", title = "COLLECTIONS",
expanded = collectionsExpanded, expanded = expandedSection == "collections",
onToggle = { collectionsExpanded = !collectionsExpanded }, onToggle = { expandedSection = if (expandedSection == "collections") "" else "collections" },
trailingContent = { trailingContent = {
TextButton( TextButton(
onClick = { onClick = {
@ -699,8 +697,8 @@ fun FeedScreen(
// Tags populaires - Accordéon // Tags populaires - Accordéon
AccordionSection( AccordionSection(
title = "TAGS POPULAIRES", title = "TAGS POPULAIRES",
expanded = tagsExpanded, expanded = expandedSection == "tags",
onToggle = { tagsExpanded = !tagsExpanded }, onToggle = { expandedSection = if (expandedSection == "tags") "" else "tags" },
trailingContent = { trailingContent = {
TextButton( TextButton(
onClick = { onClick = {

View File

@ -1,4 +1,4 @@
<# <#
.SYNOPSIS .SYNOPSIS
Script de build et de versioning pour ShaarIt. Script de build et de versioning pour ShaarIt.
.DESCRIPTION .DESCRIPTION

View File

@ -1,3 +1,3 @@
#Thu Feb 12 09:29:54 2026 #Thu Feb 12 10:28:52 2026
VERSION_NAME=1.2.0 VERSION_NAME=1.2.3
VERSION_CODE=8 VERSION_CODE=11