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()
|
.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||||
return collection
|
return collection
|
||||||
.filter { it.idHex in channel.notes.keys }
|
.filter { it.idHex in channel.notes.keys && account.isAcceptable(it) }
|
||||||
.filter { 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()
|
return collection.sortedBy { it.createdAt() }.reversed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,22 +31,22 @@ object ChatroomFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
.reversed()
|
.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||||
val myAccount = account
|
val myAccount = account
|
||||||
val myUser = withUser
|
val myUser = withUser
|
||||||
|
|
||||||
if (myAccount == null || myUser == null) return emptyList()
|
if (myAccount == null || myUser == null) return emptySet()
|
||||||
|
|
||||||
val messages = myAccount
|
val messages = myAccount
|
||||||
.userProfile()
|
.userProfile()
|
||||||
.privateChatrooms[myUser] ?: return emptyList()
|
.privateChatrooms[myUser] ?: return emptySet()
|
||||||
|
|
||||||
return collection
|
return collection
|
||||||
.filter { it in messages.roomMessages }
|
.filter { it in messages.roomMessages && account?.isAcceptable(it) == true }
|
||||||
.filter { 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()
|
return collection.sortedBy { it.createdAt() }.reversed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,15 @@ abstract class FeedFilter<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class AdditiveFeedFilter<T> : FeedFilter<T>() {
|
abstract class AdditiveFeedFilter<T> : FeedFilter<T>() {
|
||||||
abstract fun applyFilter(collection: Set<T>): List<T>
|
abstract fun applyFilter(collection: Set<T>): Set<T>
|
||||||
abstract fun sort(collection: List<T>): List<T>
|
abstract fun sort(collection: Set<T>): List<T>
|
||||||
|
|
||||||
@OptIn(ExperimentalTime::class)
|
@OptIn(ExperimentalTime::class)
|
||||||
fun updateListWith(oldList: List<T>, newItems: Set<T>): List<T> {
|
fun updateListWith(oldList: List<T>, newItems: Set<T>): List<T> {
|
||||||
val (feed, elapsed) = measureTimedValue {
|
val (feed, elapsed) = measureTimedValue {
|
||||||
val newItemsToBeAdded = applyFilter(newItems)
|
val newItemsToBeAdded = applyFilter(newItems)
|
||||||
if (newItemsToBeAdded.isNotEmpty()) {
|
if (newItemsToBeAdded.isNotEmpty()) {
|
||||||
val newList = oldList + newItemsToBeAdded
|
val newList = oldList.toSet() + newItemsToBeAdded
|
||||||
sort(newList).take(1000)
|
sort(newList).take(1000)
|
||||||
} else {
|
} else {
|
||||||
oldList
|
oldList
|
||||||
|
@ -15,11 +15,11 @@ object GlobalFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
return sort(notes + longFormNotes)
|
return sort(notes + longFormNotes)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||||
return innerApplyFilter(collection)
|
return innerApplyFilter(collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||||
val followChannels = account.followingChannels
|
val followChannels = account.followingChannels
|
||||||
val followUsers = account.followingKeySet()
|
val followUsers = account.followingKeySet()
|
||||||
val now = System.currentTimeMillis() / 1000
|
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.
|
// 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
|
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()
|
return collection.sortedBy { it.createdAt() }.reversed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,12 @@ object HashtagFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
return sort(innerApplyFilter(LocalCache.notes.values))
|
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)
|
return applyFilter(collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||||
val myTag = tag ?: return emptyList()
|
val myTag = tag ?: return emptySet()
|
||||||
|
|
||||||
return collection
|
return collection
|
||||||
.asSequence()
|
.asSequence()
|
||||||
@ -40,10 +40,10 @@ object HashtagFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
it.event?.isTaggedHash(myTag) == true
|
it.event?.isTaggedHash(myTag) == true
|
||||||
}
|
}
|
||||||
.filter { account.isAcceptable(it) }
|
.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()
|
return collection.sortedBy { it.createdAt() }.reversed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,11 @@ object HomeConversationsFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
return sort(innerApplyFilter(LocalCache.notes.values))
|
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)
|
return innerApplyFilter(collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||||
val user = account.userProfile()
|
val user = account.userProfile()
|
||||||
val followingKeySet = user.cachedFollowingKeySet()
|
val followingKeySet = user.cachedFollowingKeySet()
|
||||||
val followingTagSet = user.cachedFollowingTagSet()
|
val followingTagSet = user.cachedFollowingTagSet()
|
||||||
@ -31,10 +31,10 @@ object HomeConversationsFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
it.author?.let { !account.isHidden(it) } ?: true &&
|
it.author?.let { !account.isHidden(it) } ?: true &&
|
||||||
!it.isNewThread()
|
!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()
|
return collection.sortedBy { it.createdAt() }.reversed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,11 @@ object HomeNewThreadFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
return sort(notes + longFormNotes)
|
return sort(notes + longFormNotes)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyFilter(collection: Set<Note>): List<Note> {
|
override fun applyFilter(collection: Set<Note>): Set<Note> {
|
||||||
return innerApplyFilter(collection)
|
return innerApplyFilter(collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||||
val user = account.userProfile()
|
val user = account.userProfile()
|
||||||
val followingKeySet = user.cachedFollowingKeySet()
|
val followingKeySet = user.cachedFollowingKeySet()
|
||||||
val followingTagSet = user.cachedFollowingTagSet()
|
val followingTagSet = user.cachedFollowingTagSet()
|
||||||
@ -36,10 +36,10 @@ object HomeNewThreadFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
it.author?.let { !account.isHidden(it.pubkeyHex) } ?: true &&
|
it.author?.let { !account.isHidden(it.pubkeyHex) } ?: true &&
|
||||||
it.isNewThread()
|
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()
|
return collection.sortedBy { it.createdAt() }.reversed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,11 @@ object NotificationFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
return sort(innerApplyFilter(LocalCache.notes.values))
|
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)
|
return innerApplyFilter(collection)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun innerApplyFilter(collection: Collection<Note>): List<Note> {
|
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
|
||||||
val loggedInUser = account.userProfile()
|
val loggedInUser = account.userProfile()
|
||||||
val loggedInUserHex = loggedInUser.pubkeyHex
|
val loggedInUserHex = loggedInUser.pubkeyHex
|
||||||
|
|
||||||
@ -31,10 +31,10 @@ object NotificationFeedFilter : AdditiveFeedFilter<Note>() {
|
|||||||
it.event?.isTaggedUser(loggedInUserHex) ?: false &&
|
it.event?.isTaggedUser(loggedInUserHex) ?: false &&
|
||||||
(it.author == null || !account.isHidden(it.author!!.pubkeyHex)) &&
|
(it.author == null || !account.isHidden(it.author!!.pubkeyHex)) &&
|
||||||
tagsAnEventByUser(it, loggedInUser)
|
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()
|
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.PrivateDmEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.ReactionEvent
|
import com.vitorpamplona.amethyst.service.model.ReactionEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
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.components.BundledUpdate
|
||||||
|
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -29,7 +31,7 @@ import kotlin.time.measureTimedValue
|
|||||||
|
|
||||||
class NotificationViewModel : CardFeedViewModel(NotificationFeedFilter)
|
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)
|
private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading)
|
||||||
val feedContent = _feedContent.asStateFlow()
|
val feedContent = _feedContent.asStateFlow()
|
||||||
|
|
||||||
@ -45,9 +47,9 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
|||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun refreshSuspended() {
|
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 lastNotesCopy = if (thisAccount == lastAccount) lastNotes else null
|
||||||
|
|
||||||
val oldNotesState = _feedContent.value
|
val oldNotesState = _feedContent.value
|
||||||
@ -55,18 +57,18 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
|||||||
val newCards = convertToCard(notes.minus(lastNotesCopy))
|
val newCards = convertToCard(notes.minus(lastNotesCopy))
|
||||||
if (newCards.isNotEmpty()) {
|
if (newCards.isNotEmpty()) {
|
||||||
lastNotes = notes
|
lastNotes = notes
|
||||||
lastAccount = (dataSource as? NotificationFeedFilter)?.account
|
lastAccount = (localFilter as? NotificationFeedFilter)?.account
|
||||||
updateFeed((oldNotesState.feed.value + newCards).distinctBy { it.id() }.sortedBy { it.createdAt() }.reversed())
|
updateFeed((oldNotesState.feed.value + newCards).distinctBy { it.id() }.sortedBy { it.createdAt() }.reversed())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val cards = convertToCard(notes)
|
val cards = convertToCard(notes)
|
||||||
lastNotes = notes
|
lastNotes = notes
|
||||||
lastAccount = (dataSource as? NotificationFeedFilter)?.account
|
lastAccount = (localFilter as? NotificationFeedFilter)?.account
|
||||||
updateFeed(cards)
|
updateFeed(cards)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertToCard(notes: List<Note>): List<Card> {
|
private fun convertToCard(notes: Collection<Note>): List<Card> {
|
||||||
val reactionsPerEvent = mutableMapOf<Note, MutableList<Note>>()
|
val reactionsPerEvent = mutableMapOf<Note, MutableList<Note>>()
|
||||||
notes
|
notes
|
||||||
.filter { it.event is ReactionEvent }
|
.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)
|
@OptIn(ExperimentalTime::class)
|
||||||
private val bundler = BundledUpdate(250, Dispatchers.IO) {
|
private val bundler = BundledUpdate(250, Dispatchers.IO) {
|
||||||
// adds the time to perform the refresh into this delay
|
// 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")
|
Log.d("Time", "${this.javaClass.simpleName} Card update $elapsed")
|
||||||
}
|
}
|
||||||
|
private val bundlerInsert = BundledInsert<Set<Note>>(250, Dispatchers.IO)
|
||||||
|
|
||||||
fun invalidateData() {
|
fun invalidateData() {
|
||||||
bundler.invalidate()
|
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 ->
|
private val cacheListener: (Set<Note>) -> Unit = { newNotes ->
|
||||||
invalidateData()
|
if (localFilter is AdditiveFeedFilter && _feedContent.value is CardFeedState.Loaded) {
|
||||||
|
invalidateInsertData(newNotes)
|
||||||
|
} else {
|
||||||
|
// Refresh Everything
|
||||||
|
invalidateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
Loading…
Reference in New Issue
Block a user