diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/dal/GitRepositoriesFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/dal/GitRepositoriesFeedFilter.kt index abdd4b4335..0f383daee6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/dal/GitRepositoriesFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/dal/GitRepositoriesFeedFilter.kt @@ -48,18 +48,36 @@ class GitRepositoriesFeedFilter( override fun showHiddenKey(): Boolean = followList().wantsToSeeNegativeStuff() override fun feed(): List { - val params = buildFilterParams(account) - val notes = - LocalCache.addressables.filterIntoSet(GitRepositoryEvent.KIND) { _, it -> - val noteEvent = it.event - noteEvent is GitRepositoryEvent && params.match(noteEvent, it.relays) + if (followList() == TopFilter.Mine) { + val me = account.userProfile().pubkeyHex + LocalCache.addressables.filterIntoSet(GitRepositoryEvent.KIND) { _, it -> isMine(it, me) } + } else { + val params = buildFilterParams(account) + LocalCache.addressables.filterIntoSet(GitRepositoryEvent.KIND) { _, it -> + val noteEvent = it.event + noteEvent is GitRepositoryEvent && params.match(noteEvent, it.relays) + } } return sort(notes) } - override fun applyFilter(newItems: Set): Set = innerApplyFilter(newItems) + override fun applyFilter(newItems: Set): Set { + if (followList() == TopFilter.Mine) { + val me = account.userProfile().pubkeyHex + return newItems.filterTo(HashSet()) { isMine(it, me) } + } + return innerApplyFilter(newItems) + } + + private fun isMine( + note: Note, + me: String, + ): Boolean { + val noteEvent = note.event + return noteEvent is GitRepositoryEvent && noteEvent.pubKey == me + } fun buildFilterParams(account: Account): FilterByListParams = FilterByListParams.create( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/datasource/GitRepositoriesSubAssembler.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/datasource/GitRepositoriesSubAssembler.kt index 6fb5fc19c4..1c837ee555 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/datasource/GitRepositoriesSubAssembler.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/datasource/GitRepositoriesSubAssembler.kt @@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.model.TopFilter import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.relayClient.eoseManagers.PerUserAndFollowListEoseManager import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap +import com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies.filterGitRepositoriesMine import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter import com.vitorpamplona.quartz.nip01Core.relay.client.subscriptions.Subscription @@ -42,6 +43,13 @@ class GitRepositoriesSubAssembler( key: GitRepositoriesQueryState, since: SincePerRelayMap?, ): List { + // "Mine" bypasses the follow-list machinery: query the user's own repositories by author + // against their outbox relays (same pattern as music/badges), because the shared + // TopFilter.Mine flow falls back to all-follows. + if (key.listName() == TopFilter.Mine) { + val outbox = key.account.outboxRelays.flow.value + return filterGitRepositoriesMine(key.account.userProfile().pubkeyHex, outbox, since) + } val feedSettings = key.followsPerRelay() return makeGitRepositoriesFilter(feedSettings, since, key.feedStates.gitRepositoriesFeed.lastNoteCreatedAtIfFilled()) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/datasource/subassemblies/FilterGitRepositoriesMine.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/datasource/subassemblies/FilterGitRepositoriesMine.kt new file mode 100644 index 0000000000..7e6396f168 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepositories/datasource/subassemblies/FilterGitRepositoriesMine.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepositories.datasource.subassemblies + +import com.vitorpamplona.amethyst.service.relays.SincePerRelayMap +import com.vitorpamplona.quartz.nip01Core.core.HexKey +import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter +import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter +import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent + +/** + * Builds the relay filters for the "Mine" repository selector: the user's own repository + * announcements, queried by author against their own outbox relays. Mirrors + * `filterNappletsMine` / `filterMusicEventsMine` — the only correct source for "my own" + * content, since the shared `TopFilter.Mine` flow falls back to all-follows. + */ +fun filterGitRepositoriesMine( + pubkey: HexKey, + relays: Set, + since: SincePerRelayMap?, +): List { + if (relays.isEmpty() || pubkey.isEmpty()) return emptyList() + val authors = listOf(pubkey) + return relays.map { relay -> + RelayBasedFilter( + relay = relay, + filter = + Filter( + kinds = listOf(GitRepositoryEvent.KIND), + authors = authors, + limit = 200, + since = since?.get(relay)?.time, + ), + ) + } +}