Skip to content

Commit 5298a7a

Browse files
committed
feat: implement topic-based repository discovery and filtering
- Introduce `TopicCategory` with predefined sets of matching topics and search keywords for Privacy, Media, Productivity, Networking, and Dev Tools. - Add `SwitchTopic` action to `HomeViewModel` to allow filtering the discovery feed by specific categories. - Implement a topic supplement loading mechanism in `HomeViewModel` that combines standard category results with keyword-based GitHub searches for better discovery. - Enhance `HomeRepository` and its implementation to support searching repositories by topic keywords with caching. - Update `HomeRoot` UI to include a horizontally scrollable list of `FilterChip` components for topic selection. - Implement a scroll-aware header in `HomeRoot` that animates the visibility of the top bar and filter chips based on scroll direction. - Add localized strings and icons for the new topic categories. - Refactor repository-to-UI mapping into a reusable `mapReposToUi` function.
1 parent a482aec commit 5298a7a

9 files changed

Lines changed: 424 additions & 51 deletions

File tree

core/presentation/src/commonMain/composeResources/values/strings.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,12 @@
270270
<string name="home_category_hot_release">Hot release</string>
271271
<string name="home_category_most_popular">Most popular</string>
272272

273+
<string name="home_topic_privacy">Privacy</string>
274+
<string name="home_topic_media">Media</string>
275+
<string name="home_topic_productivity">Productivity</string>
276+
<string name="home_topic_networking">Network</string>
277+
<string name="home_topic_dev_tools">Dev Tools</string>
278+
273279
<string name="home_finding_repositories">Finding repositories...</string>
274280
<string name="home_loading_more">Loading more...</string>
275281
<string name="home_no_more_repositories">No more repositories</string>

feature/home/data/src/commonMain/kotlin/zed/rainxch/home/data/repository/HomeRepositoryImpl.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,39 @@ class HomeRepositoryImpl(
269269
)
270270
}.flowOn(Dispatchers.IO)
271271

272+
override fun searchByTopic(
273+
searchKeywords: String,
274+
platform: DiscoveryPlatform,
275+
page: Int,
276+
): Flow<PaginatedDiscoveryRepositories> =
277+
flow {
278+
val cacheCategory = "topic_${searchKeywords.hashCode()}"
279+
val localCached =
280+
cacheManager.get<PaginatedDiscoveryRepositories>(
281+
cacheKey(
282+
category = cacheCategory,
283+
requestedPlatform = platform,
284+
page = page,
285+
),
286+
)
287+
if (localCached != null && localCached.repos.isNotEmpty()) {
288+
logger.debug("Using cached topic repos: ${localCached.repos.size}")
289+
emit(localCached)
290+
return@flow
291+
}
292+
293+
emitAll(
294+
searchReposWithInstallersFlow(
295+
platform = platform,
296+
baseQuery = "$searchKeywords in:name,description,topics stars:>10 archived:false",
297+
sort = "stars",
298+
order = "desc",
299+
startPage = page,
300+
category = cacheCategory,
301+
),
302+
)
303+
}.flowOn(Dispatchers.IO)
304+
272305
private fun searchReposWithInstallersFlow(
273306
platform: DiscoveryPlatform,
274307
baseQuery: String,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package zed.rainxch.home.domain.model
2+
3+
enum class TopicCategory(
4+
val matchTopics: Set<String>,
5+
val searchKeywords: String,
6+
) {
7+
PRIVACY(
8+
matchTopics = setOf(
9+
"privacy", "security", "encryption", "vpn", "firewall",
10+
"password-manager", "privacy-tools", "e2ee", "secure",
11+
"anonymity", "tor", "pgp", "2fa", "auth",
12+
),
13+
searchKeywords = "privacy security encryption vpn firewall",
14+
),
15+
MEDIA(
16+
matchTopics = setOf(
17+
"music-player", "video-player", "media", "podcast", "streaming",
18+
"audio", "video", "media-player", "music", "player",
19+
"mpv", "vlc", "recorder", "screen-recorder", "gallery",
20+
),
21+
searchKeywords = "music-player video-player media podcast audio",
22+
),
23+
PRODUCTIVITY(
24+
matchTopics = setOf(
25+
"productivity", "file-manager", "notes", "launcher", "keyboard",
26+
"browser", "calendar", "todo", "note-taking", "editor",
27+
"organizer", "task-manager", "markdown", "writing",
28+
),
29+
searchKeywords = "productivity file-manager notes launcher browser",
30+
),
31+
NETWORKING(
32+
matchTopics = setOf(
33+
"proxy", "dns", "ad-blocker", "torrent", "downloader",
34+
"network", "ssh", "wireguard", "adblock", "download-manager",
35+
"firewall", "socks5", "http-proxy", "p2p", "ftp",
36+
),
37+
searchKeywords = "proxy dns ad-blocker torrent downloader network",
38+
),
39+
DEV_TOOLS(
40+
matchTopics = setOf(
41+
"terminal", "developer-tools", "git-client", "editor", "cli",
42+
"ide", "devtools", "code-editor", "terminal-emulator", "development",
43+
"adb", "debugger", "api-client", "shell", "sdk",
44+
),
45+
searchKeywords = "terminal developer-tools git-client code-editor cli",
46+
),
47+
;
48+
49+
fun matchesRepo(
50+
topics: List<String>?,
51+
description: String?,
52+
name: String?,
53+
): Boolean {
54+
val repoTopics = topics?.map { it.lowercase() }.orEmpty()
55+
if (repoTopics.any { it in matchTopics }) return true
56+
57+
val desc = description?.lowercase() ?: ""
58+
val repoName = name?.lowercase() ?: ""
59+
val keywords = searchKeywords.split(" ")
60+
return keywords.any { keyword ->
61+
desc.contains(keyword) || repoName.contains(keyword)
62+
}
63+
}
64+
}

feature/home/domain/src/commonMain/kotlin/zed/rainxch/home/domain/repository/HomeRepository.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@ interface HomeRepository {
1919
platform: DiscoveryPlatform,
2020
page: Int,
2121
): Flow<PaginatedDiscoveryRepositories>
22+
23+
fun searchByTopic(
24+
searchKeywords: String,
25+
platform: DiscoveryPlatform,
26+
page: Int,
27+
): Flow<PaginatedDiscoveryRepositories>
2228
}

feature/home/presentation/src/commonMain/kotlin/zed/rainxch/home/presentation/HomeAction.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package zed.rainxch.home.presentation
33
import zed.rainxch.core.domain.model.DiscoveryPlatform
44
import zed.rainxch.core.presentation.model.GithubRepoSummaryUi
55
import zed.rainxch.home.domain.model.HomeCategory
6+
import zed.rainxch.home.domain.model.TopicCategory
67

78
sealed interface HomeAction {
89
data object Refresh : HomeAction
@@ -27,6 +28,10 @@ sealed interface HomeAction {
2728
val category: HomeCategory,
2829
) : HomeAction
2930

31+
data class SwitchTopic(
32+
val topic: TopicCategory?,
33+
) : HomeAction
34+
3035
data class SwitchFilterPlatform(
3136
val platform: DiscoveryPlatform,
3237
) : HomeAction

0 commit comments

Comments
 (0)