mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2024-09-30 00:40:49 +00:00
Adapting interfaces for the additive filter.
This commit is contained in:
parent
abdad7fbea
commit
6981fe8f8a
@ -23,13 +23,13 @@ object ChannelFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
.reversed()
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return collection
|
||||
.filter { it.idHex in channel.notes.keys }
|
||||
.filter { account.isAcceptable(it) }
|
||||
.filter { it.idHex in channel.notes.keys && account.isAcceptable(it) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: List<Note>): List<Note> {
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedBy { it.createdAt() }.reversed()
|
||||
}
|
||||
}
|
||||
|
@ -31,22 +31,22 @@ object ChatroomFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
.reversed()
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
val myAccount = account
|
||||
val myUser = withUser
|
||||
|
||||
if (myAccount == null || myUser == null) return emptyList()
|
||||
if (myAccount == null || myUser == null) return emptySet()
|
||||
|
||||
val messages = myAccount
|
||||
.userProfile()
|
||||
.privateChatrooms[myUser] ?: return emptyList()
|
||||
.privateChatrooms[myUser] ?: return emptySet()
|
||||
|
||||
return collection
|
||||
.filter { it in messages.roomMessages }
|
||||
.filter { account?.isAcceptable(it) == true }
|
||||
.filter { it in messages.roomMessages && account?.isAcceptable(it) == true }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: List<Note>): List<Note> {
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedBy { it.createdAt() }.reversed()
|
||||
}
|
||||
}
|
||||
|
@ -19,15 +19,15 @@ abstract class FeedFilter<T> {
|
||||
}
|
||||
|
||||
abstract class AdditiveFeedFilter<T> : FeedFilter<T>() {
|
||||
abstract fun applyFilter(collection: Set<T>): List<T>
|
||||
abstract fun sort(collection: List<T>): List<T>
|
||||
abstract fun applyFilter(collection: Set<T>): Set<T>
|
||||
abstract fun sort(collection: Set<T>): List<T>
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
fun updateListWith(oldList: List<T>, newItems: Set<T>): List<T> {
|
||||
val (feed, elapsed) = measureTimedValue {
|
||||
val newItemsToBeAdded = applyFilter(newItems)
|
||||
if (newItemsToBeAdded.isNotEmpty()) {
|
||||
val newList = oldList + newItemsToBeAdded
|
||||
val newList = oldList.toSet() + newItemsToBeAdded
|
||||
sort(newList).take(1000)
|
||||
} else {
|
||||
oldList
|
||||
|
@ -15,11 +15,11 @@ object GlobalFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
return sort(notes + longFormNotes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val followChannels = account.followingChannels
|
||||
val followUsers = account.followingKeySet()
|
||||
val now = System.currentTimeMillis() / 1000
|
||||
@ -41,10 +41,10 @@ object GlobalFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
// Do not show notes with the creation time exceeding the current time, as they will always stay at the top of the global feed, which is cheating.
|
||||
it.createdAt()!! <= now
|
||||
}
|
||||
.toList()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: List<Note>): List<Note> {
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedBy { it.createdAt() }.reversed()
|
||||
}
|
||||
}
|
||||
|
@ -21,12 +21,12 @@ object HashtagFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
return sort(innerApplyFilter(LocalCache.notes.values))
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return applyFilter(collection)
|
||||
}
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
||||
val myTag = tag ?: return emptyList()
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val myTag = tag ?: return emptySet()
|
||||
|
||||
return collection
|
||||
.asSequence()
|
||||
@ -40,10 +40,10 @@ object HashtagFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
it.event?.isTaggedHash(myTag) == true
|
||||
}
|
||||
.filter { account.isAcceptable(it) }
|
||||
.toList()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: List<Note>): List<Note> {
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedBy { it.createdAt() }.reversed()
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,11 @@ object HomeConversationsFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
return sort(innerApplyFilter(LocalCache.notes.values))
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val user = account.userProfile()
|
||||
val followingKeySet = user.cachedFollowingKeySet()
|
||||
val followingTagSet = user.cachedFollowingTagSet()
|
||||
@ -31,10 +31,10 @@ object HomeConversationsFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
it.author?.let { !account.isHidden(it) } ?: true &&
|
||||
!it.isNewThread()
|
||||
}
|
||||
.toList()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: List<Note>): List<Note> {
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedBy { it.createdAt() }.reversed()
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,11 @@ object HomeNewThreadFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
return sort(notes + longFormNotes)
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val user = account.userProfile()
|
||||
val followingKeySet = user.cachedFollowingKeySet()
|
||||
val followingTagSet = user.cachedFollowingTagSet()
|
||||
@ -36,10 +36,10 @@ object HomeNewThreadFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
it.author?.let { !account.isHidden(it.pubkeyHex) } ?: true &&
|
||||
it.isNewThread()
|
||||
}
|
||||
.toList()
|
||||
.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: List<Note>): List<Note> {
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedBy { it.createdAt() }.reversed()
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,11 @@ object NotificationFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
return sort(innerApplyFilter(LocalCache.notes.values))
|
||||
}
|
||||
|
||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
||||
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||
return innerApplyFilter(collection)
|
||||
}
|
||||
|
||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
||||
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||
val loggedInUser = account.userProfile()
|
||||
val loggedInUserHex = loggedInUser.pubkeyHex
|
||||
|
||||
@ -31,10 +31,10 @@ object NotificationFeedFilter : AdditiveFeedFilter<Note>() {
|
||||
it.event?.isTaggedUser(loggedInUserHex) ?: false &&
|
||||
(it.author == null || !account.isHidden(it.author!!.pubkeyHex)) &&
|
||||
tagsAnEventByUser(it, loggedInUser)
|
||||
}
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
override fun sort(collection: List<Note>): List<Note> {
|
||||
override fun sort(collection: Set<Note>): List<Note> {
|
||||
return collection.sortedBy { it.createdAt() }.reversed()
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,9 @@ import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
||||
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
|
||||
import com.vitorpamplona.amethyst.service.model.ReactionEvent
|
||||
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
||||
import com.vitorpamplona.amethyst.ui.components.BundledInsert
|
||||
import com.vitorpamplona.amethyst.ui.components.BundledUpdate
|
||||
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
|
||||
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -29,7 +31,7 @@ import kotlin.time.measureTimedValue
|
||||
|
||||
class NotificationViewModel : CardFeedViewModel(NotificationFeedFilter)
|
||||
|
||||
open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
||||
open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
|
||||
private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading)
|
||||
val feedContent = _feedContent.asStateFlow()
|
||||
|
||||
@ -45,9 +47,9 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
||||
|
||||
@Synchronized
|
||||
private fun refreshSuspended() {
|
||||
val notes = dataSource.loadTop()
|
||||
val notes = localFilter.loadTop()
|
||||
|
||||
val thisAccount = (dataSource as? NotificationFeedFilter)?.account
|
||||
val thisAccount = (localFilter as? NotificationFeedFilter)?.account
|
||||
val lastNotesCopy = if (thisAccount == lastAccount) lastNotes else null
|
||||
|
||||
val oldNotesState = _feedContent.value
|
||||
@ -55,18 +57,18 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
||||
val newCards = convertToCard(notes.minus(lastNotesCopy))
|
||||
if (newCards.isNotEmpty()) {
|
||||
lastNotes = notes
|
||||
lastAccount = (dataSource as? NotificationFeedFilter)?.account
|
||||
lastAccount = (localFilter as? NotificationFeedFilter)?.account
|
||||
updateFeed((oldNotesState.feed.value + newCards).distinctBy { it.id() }.sortedBy { it.createdAt() }.reversed())
|
||||
}
|
||||
} else {
|
||||
val cards = convertToCard(notes)
|
||||
lastNotes = notes
|
||||
lastAccount = (dataSource as? NotificationFeedFilter)?.account
|
||||
lastAccount = (localFilter as? NotificationFeedFilter)?.account
|
||||
updateFeed(cards)
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertToCard(notes: List<Note>): List<Card> {
|
||||
private fun convertToCard(notes: Collection<Note>): List<Card> {
|
||||
val reactionsPerEvent = mutableMapOf<Note, MutableList<Note>>()
|
||||
notes
|
||||
.filter { it.event is ReactionEvent }
|
||||
@ -171,6 +173,28 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshFromOldState(newItems: Set<Note>) {
|
||||
val oldNotesState = _feedContent.value
|
||||
|
||||
val thisAccount = (localFilter as? NotificationFeedFilter)?.account
|
||||
val lastNotesCopy = if (thisAccount == lastAccount) lastNotes else null
|
||||
|
||||
if (lastNotesCopy != null && localFilter is AdditiveFeedFilter && oldNotesState is CardFeedState.Loaded) {
|
||||
val filteredNewList = localFilter.applyFilter(newItems)
|
||||
val actuallyNew = filteredNewList.minus(lastNotesCopy)
|
||||
|
||||
val newCards = convertToCard(actuallyNew)
|
||||
if (newCards.isNotEmpty()) {
|
||||
lastNotes = lastNotesCopy + newItems
|
||||
lastAccount = (localFilter as? NotificationFeedFilter)?.account
|
||||
updateFeed((oldNotesState.feed.value + newCards).distinctBy { it.id() }.sortedBy { it.createdAt() }.reversed())
|
||||
}
|
||||
} else {
|
||||
// Refresh Everything
|
||||
refreshSuspended()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
private val bundler = BundledUpdate(250, Dispatchers.IO) {
|
||||
// adds the time to perform the refresh into this delay
|
||||
@ -180,13 +204,29 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
||||
}
|
||||
Log.d("Time", "${this.javaClass.simpleName} Card update $elapsed")
|
||||
}
|
||||
private val bundlerInsert = BundledInsert<Set<Note>>(250, Dispatchers.IO)
|
||||
|
||||
fun invalidateData() {
|
||||
bundler.invalidate()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
fun invalidateInsertData(newItems: Set<Note>) {
|
||||
bundlerInsert.invalidateList(newItems) {
|
||||
val (value, elapsed) = measureTimedValue {
|
||||
refreshFromOldState(it.flatten().toSet())
|
||||
}
|
||||
Log.d("Time", "${this.javaClass.simpleName} Card additive update $elapsed")
|
||||
}
|
||||
}
|
||||
|
||||
private val cacheListener: (Set<Note>) -> Unit = { newNotes ->
|
||||
invalidateData()
|
||||
if (localFilter is AdditiveFeedFilter && _feedContent.value is CardFeedState.Loaded) {
|
||||
invalidateInsertData(newNotes)
|
||||
} else {
|
||||
// Refresh Everything
|
||||
invalidateData()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
|
Loading…
Reference in New Issue
Block a user