diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 84673489a..0f94e7ffa 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -2043,7 +2043,10 @@ object LocalCache { } } - fun findUsersStartingWith(username: String): List { + fun findUsersStartingWith( + username: String, + forAccount: Account?, + ): List { checkNotInMainThread() val key = decodePublicKeyAsHexOrNull(username) @@ -2056,13 +2059,19 @@ object LocalCache { } return users.filter { _, user: User -> - (user.anyNameStartsWith(username)) || - user.pubkeyHex.startsWith(username, true) || - user.pubkeyNpub().startsWith(username, true) + ( + (user.anyNameStartsWith(username)) || + user.pubkeyHex.startsWith(username, true) || + user.pubkeyNpub().startsWith(username, true) + ) && + (forAccount == null || (!forAccount.isHidden(user) && !user.containsAny(forAccount.flowHiddenUsers.value.hiddenWordsCase))) } } - fun findNotesStartingWith(text: String): List { + fun findNotesStartingWith( + text: String, + forAccount: Account, + ): List { checkNotInMainThread() val key = decodeEventIdAsHexOrNull(text) @@ -2089,11 +2098,19 @@ object LocalCache { note.idHex.startsWith(text, true) || note.idNote().startsWith(text, true) ) { - return@filter true + if (!note.isHiddenFor(forAccount.flowHiddenUsers.value)) { + return@filter true + } else { + return@filter false + } } if (note.event?.isContentEncoded() == false) { - return@filter note.event?.content()?.contains(text, true) ?: false + if (!note.isHiddenFor(forAccount.flowHiddenUsers.value)) { + return@filter note.event?.content()?.contains(text, true) ?: false + } else { + return@filter false + } } return@filter false @@ -2112,11 +2129,19 @@ object LocalCache { if (addressable.event?.matchTag1With(text) == true || addressable.idHex.startsWith(text, true) ) { - return@filter true + if (!addressable.isHiddenFor(forAccount.flowHiddenUsers.value)) { + return@filter true + } else { + return@filter false + } } if (addressable.event?.isContentEncoded() == false) { - return@filter addressable.event?.content()?.contains(text, true) ?: false + if (!addressable.isHiddenFor(forAccount.flowHiddenUsers.value)) { + return@filter addressable.event?.content()?.contains(text, true) ?: false + } else { + return@filter false + } } return@filter false diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt index 6c661d56c..bb88bf0b1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt @@ -771,29 +771,11 @@ open class Note( return true } - if (author?.toBestDisplayName()?.containsAny(accountChoices.hiddenWordsCase) == true) { + if (thisEvent.anyHashTag { it.containsAny(accountChoices.hiddenWordsCase) }) { return true } - if (author?.profilePicture()?.containsAny(accountChoices.hiddenWordsCase) == true) { - return true - } - - if (author?.info?.banner?.containsAny(accountChoices.hiddenWordsCase) == true) { - return true - } - - if (author?.info?.about?.containsAny(accountChoices.hiddenWordsCase) == true) { - return true - } - - if (author?.info?.lud06?.containsAny(accountChoices.hiddenWordsCase) == true) { - return true - } - - if (author?.info?.lud16?.containsAny(accountChoices.hiddenWordsCase) == true) { - return true - } + if (author?.containsAny(accountChoices.hiddenWordsCase) == true) return true } return false diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/User.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/User.kt index e66d65e7a..016a0eef6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/User.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/User.kt @@ -45,6 +45,8 @@ import com.vitorpamplona.quartz.events.MetadataEvent import com.vitorpamplona.quartz.events.ReportEvent import com.vitorpamplona.quartz.events.UserMetadata import com.vitorpamplona.quartz.events.toImmutableListOfLists +import com.vitorpamplona.quartz.utils.DualCase +import com.vitorpamplona.quartz.utils.containsAny import kotlinx.collections.immutable.persistentSetOf import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -370,6 +372,36 @@ class User( (it.event as ReportEvent).reportedAuthor().any { it.reportType == type } } != null + fun containsAny(hiddenWordsCase: List): Boolean { + if (hiddenWordsCase.isEmpty()) return false + + if (toBestDisplayName().containsAny(hiddenWordsCase)) { + return true + } + + if (profilePicture()?.containsAny(hiddenWordsCase) == true) { + return true + } + + if (info?.banner?.containsAny(hiddenWordsCase) == true) { + return true + } + + if (info?.about?.containsAny(hiddenWordsCase) == true) { + return true + } + + if (info?.lud06?.containsAny(hiddenWordsCase) == true) { + return true + } + + if (info?.lud16?.containsAny(hiddenWordsCase) == true) { + return true + } + + return false + } + fun anyNameStartsWith(username: String): Boolean = info?.anyNameStartsWith(username) ?: false var liveSet: UserLiveSet? = null diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt index a287f90ca..788882e92 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/EditPostViewModel.kt @@ -274,7 +274,7 @@ open class EditPostViewModel : ViewModel() { viewModelScope.launch(Dispatchers.IO) { userSuggestions = LocalCache - .findUsersStartingWith(lastWord.removePrefix("@")) + .findUsersStartingWith(lastWord.removePrefix("@"), account) .sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() }, { it.pubkeyHex })) .reversed() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index 997c3140d..d6a34b59e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -1002,7 +1002,7 @@ open class NewPostViewModel : ViewModel() { viewModelScope.launch(Dispatchers.IO) { userSuggestions = LocalCache - .findUsersStartingWith(lastWord.removePrefix("@")) + .findUsersStartingWith(lastWord.removePrefix("@"), account) .sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() }, { it.pubkeyHex })) .reversed() } @@ -1031,7 +1031,7 @@ open class NewPostViewModel : ViewModel() { viewModelScope.launch(Dispatchers.IO) { userSuggestions = LocalCache - .findUsersStartingWith(lastWord.removePrefix("@")) + .findUsersStartingWith(lastWord.removePrefix("@"), account) .sortedWith(compareBy({ account?.isFollowing(it) }, { it.toBestDisplayName() }, { it.pubkeyHex })) .reversed() } @@ -1059,7 +1059,7 @@ open class NewPostViewModel : ViewModel() { viewModelScope.launch(Dispatchers.IO) { userSuggestions = LocalCache - .findUsersStartingWith(lastWord.removePrefix("@")) + .findUsersStartingWith(lastWord.removePrefix("@"), account) .sortedWith( compareBy( { account?.isFollowing(it) }, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt index 2c759ec09..f1d2131cf 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/GeoHashFeedFilter.kt @@ -64,6 +64,7 @@ class GeoHashFeedFilter( it.event is AudioHeaderEvent ) && it.event?.isTaggedGeoHash(geoTag) == true && + !it.isHiddenFor(account.flowHiddenUsers.value) && account.isAcceptable(it) override fun sort(collection: Set): List = collection.sortedWith(DefaultFeedOrder) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt index 0f5c7950e..74a0b99f8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/HashtagFeedFilter.kt @@ -68,6 +68,7 @@ class HashtagFeedFilter( it.event is AudioHeaderEvent ) && it.event?.isTaggedHash(hashTag) == true && + !it.isHiddenFor(account.flowHiddenUsers.value) && account.isAcceptable(it) override fun sort(collection: Set): List = collection.sortedWith(DefaultFeedOrder) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchBarViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchBarViewModel.kt index 71bbe24c1..becdd627c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchBarViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchBarViewModel.kt @@ -75,7 +75,7 @@ class SearchBarViewModel( _hashtagResults.emit(findHashtags(searchValue)) _searchResultsUsers.emit( LocalCache - .findUsersStartingWith(searchValue) + .findUsersStartingWith(searchValue, account) .sortedWith( compareBy( { it.toBestDisplayName().startsWith(searchValue, true) }, @@ -86,7 +86,7 @@ class SearchBarViewModel( ) _searchResultsNotes.emit( LocalCache - .findNotesStartingWith(searchValue) + .findNotesStartingWith(searchValue, account) .sortedWith(compareBy({ it.createdAt() }, { it.idHex })) .reversed(), ) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt index 9c7f432d9..c6799b546 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/search/SearchScreen.kt @@ -36,7 +36,6 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -178,7 +177,7 @@ fun WatchAccountForSearchScreen(accountViewModel: AccountViewModel) { } } -@OptIn(FlowPreview::class, ExperimentalMaterial3Api::class) +@OptIn(FlowPreview::class) @Composable private fun SearchBar( searchBarViewModel: SearchBarViewModel, diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt index 5968b1452..438d059b9 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/Event.kt @@ -107,6 +107,19 @@ open class Event( } } + override fun anyHashTag(onEach: (str: String) -> Boolean) = anyTagged("t", onEach) + + private fun anyTagged( + tagName: String, + onEach: (str: String) -> Boolean, + ) = tags.any { + if (it.size > 1 && it[0] == tagName) { + onEach(it[1]) + } else { + false + } + } + override fun mapTaggedEvent(map: (eventId: HexKey) -> R) = mapTagged("e", map) override fun mapTaggedAddress(map: (address: String) -> R) = mapTagged("a", map) diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/EventInterface.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/EventInterface.kt index ea73ebae2..a014f8c6a 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/EventInterface.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/EventInterface.kt @@ -121,6 +121,8 @@ interface EventInterface { fun forEachHashTag(onEach: (eventId: HexKey) -> Unit) + fun anyHashTag(onEach: (str: String) -> Boolean): Boolean + fun mapTaggedEvent(map: (eventId: HexKey) -> R): List fun mapTaggedAddress(map: (address: String) -> R): List