Skip to content

Commit

Permalink
feat: added a search function (#647)
Browse files Browse the repository at this point in the history
* add search button

* improve search

* fix formatting

* translation

* move search into Repository

* always show search bar

* fix test
  • Loading branch information
Friendly-Banana authored Mar 3, 2025
1 parent 78d28bb commit 5e6ef96
Show file tree
Hide file tree
Showing 7 changed files with 412 additions and 292 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class FeedItemStore(
tag: String,
minReadTime: Instant,
filter: FeedListFilter,
search: String,
): Flow<Int> {
val queryString = StringBuilder()
val args = mutableListOf<Any?>()
Expand All @@ -65,7 +66,7 @@ class FeedItemStore(
append("LEFT JOIN feeds ON feed_items.feed_id = feeds.id\n")
append("WHERE\n")

rawQueryFilter(filter, args, minReadTime, feedId, tag)
rawQueryFilter(filter, search, args, minReadTime, feedId, tag)
}

return dao.getPreviewsCount(SimpleSQLiteQuery(queryString.toString(), args.toTypedArray()))
Expand All @@ -77,6 +78,7 @@ class FeedItemStore(
minReadTime: Instant,
newestFirst: Boolean,
filter: FeedListFilter,
search: String,
): Flow<PagingData<FeedListItem>> =
Pager(
config =
Expand All @@ -95,7 +97,7 @@ class FeedItemStore(
append("LEFT JOIN feeds ON feed_items.feed_id = feeds.id\n")
append("WHERE\n")

rawQueryFilter(filter, args, minReadTime, feedId, tag)
rawQueryFilter(filter, search, args, minReadTime, feedId, tag)

when (newestFirst) {
true -> append("ORDER BY $FEED_ITEM_LIST_SORT_ORDER_DESC\n")
Expand All @@ -112,6 +114,7 @@ class FeedItemStore(

private fun StringBuilder.rawQueryFilter(
filter: FeedListFilter,
search: String,
args: MutableList<Any?>,
minReadTime: Instant,
feedId: Long,
Expand Down Expand Up @@ -143,6 +146,18 @@ class FeedItemStore(
}

when {
search.isNotEmpty() -> {
// User probably means the literal characters
val sanitizedSearch =
search
.replace("\\", "\\\\")
.replace("%", "\\%")
.replace("_", "\\_")
append("AND (\n")
append("plain_title LIKE ?\n").also { args.add("%$sanitizedSearch%") }
append("OR plain_snippet LIKE ?\n").also { args.add("%$sanitizedSearch%") }
append(")\n")
}
onlySavedArticles -> append("AND bookmarked = 1\n")
feedId > ID_UNSET -> append("AND feed_id IS ?\n").also { args.add(feedId) }
tag.isNotEmpty() -> append("AND tag IS ?\n").also { args.add(tag) }
Expand All @@ -153,6 +168,7 @@ class FeedItemStore(
feedId: Long,
tag: String,
filter: FeedListFilter,
search: String,
minReadTime: Instant,
descending: Boolean,
cursor: FeedItemCursor,
Expand All @@ -169,7 +185,7 @@ class FeedItemStore(
append("SELECT feed_items.id FROM feed_items\n")
append("LEFT JOIN feeds ON feed_items.feed_id = feeds.id\n")
append("WHERE\n")
rawQueryFilter(filter, args, minReadTime, feedId, tag)
rawQueryFilter(filter, search, args, minReadTime, feedId, tag)
// this version of sqlite doesn't seem to support tuple comparisons
append("and (\n")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ import com.nononsenseapps.feeder.util.addDynamicShortcutToFeed
import com.nononsenseapps.feeder.util.reportShortcutToFeedUsed
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.kodein.di.DI
import org.kodein.di.DIAware
Expand Down Expand Up @@ -102,6 +104,7 @@ class Repository(
tag = tag,
minReadTime = Instant.now(),
filter = emptyFeedListFilter,
search = "",
)

fun setCurrentFeedAndTag(
Expand Down Expand Up @@ -172,6 +175,10 @@ class Repository(
}
}

val search: MutableStateFlow<String> = MutableStateFlow("")

fun searchFor(value: String) = search.update { value }

val currentArticleId: StateFlow<Long> = settingsStore.currentArticleId

fun setCurrentArticle(articleId: Long) = settingsStore.setCurrentArticle(articleId)
Expand Down Expand Up @@ -311,7 +318,8 @@ class Repository(
minReadTime,
currentSorting,
feedListFilter,
) { feedAndTag, minReadTime, currentSorting, feedListFilter ->
search,
) { feedAndTag, minReadTime, currentSorting, feedListFilter, search ->
val (feedId, tag) = feedAndTag
FeedListArgs(
feedId = feedId,
Expand All @@ -323,6 +331,7 @@ class Repository(
},
newestFirst = currentSorting == SortingOptions.NEWEST_FIRST,
filter = feedListFilter,
search = search,
)
}.flatMapLatest {
feedItemStore.getPagedFeedItemsRaw(
Expand All @@ -331,6 +340,7 @@ class Repository(
minReadTime = it.minReadTime,
newestFirst = it.newestFirst,
filter = it.filter,
search = it.search,
)
}

Expand All @@ -340,7 +350,8 @@ class Repository(
currentFeedAndTag,
minReadTime,
feedListFilter,
) { feedAndTag, minReadTime, feedListFilter ->
search,
) { feedAndTag, minReadTime, feedListFilter, search ->
val (feedId, tag) = feedAndTag
FeedListArgs(
feedId = feedId,
Expand All @@ -352,13 +363,15 @@ class Repository(
},
newestFirst = false,
filter = feedListFilter,
search = search,
)
}.flatMapLatest {
feedItemStore.getFeedItemCountRaw(
feedId = it.feedId,
tag = it.tag,
minReadTime = it.minReadTime,
filter = it.filter,
search = it.search,
)
}

Expand Down Expand Up @@ -500,6 +513,7 @@ class Repository(
feedId = feedId,
tag = tag,
filter = feedListFilter.value,
search = search.value,
minReadTime = minReadTime.value,
descending = SortingOptions.NEWEST_FIRST != currentSorting.value,
cursor = cursor,
Expand All @@ -516,6 +530,7 @@ class Repository(
feedId = feedId,
tag = tag,
filter = feedListFilter.value,
search = search.value,
minReadTime = minReadTime.value,
descending = SortingOptions.NEWEST_FIRST == currentSorting.value,
cursor = cursor,
Expand All @@ -537,6 +552,7 @@ class Repository(
tag = "",
minReadTime = Instant.EPOCH,
filter = emptyFeedListFilter,
search = "",
)

@OptIn(ExperimentalCoroutinesApi::class)
Expand Down Expand Up @@ -781,6 +797,7 @@ private data class FeedListArgs(
val newestFirst: Boolean,
val minReadTime: Instant,
val filter: FeedListFilter,
val search: String,
)

// Wrapper class because flow combine doesn't like nulls
Expand Down
Loading

0 comments on commit 5e6ef96

Please sign in to comment.