amethyst/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt

146 wiersze
5.1 KiB
Kotlin
Czysty Zwykły widok Historia

2023-01-11 18:31:20 +00:00
package com.vitorpamplona.amethyst.ui.screen
import android.util.Log
2023-01-27 22:28:59 +00:00
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
2023-01-11 18:31:20 +00:00
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.LocalCacheState
import com.vitorpamplona.amethyst.model.Note
2023-01-18 23:59:18 +00:00
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
import com.vitorpamplona.amethyst.service.NostrChatRoomDataSource
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
2023-01-11 18:31:20 +00:00
import com.vitorpamplona.amethyst.service.NostrDataSource
2023-01-18 23:59:18 +00:00
import com.vitorpamplona.amethyst.service.NostrGlobalDataSource
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
import com.vitorpamplona.amethyst.service.model.RepostEvent
2023-01-18 19:24:04 +00:00
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
2023-01-11 18:31:20 +00:00
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
2023-01-11 18:31:20 +00:00
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import nostr.postr.events.TextNoteEvent
import java.util.concurrent.atomic.AtomicBoolean
2023-01-11 18:31:20 +00:00
2023-01-18 23:59:18 +00:00
class NostrChannelFeedViewModel: FeedViewModel(NostrChannelDataSource)
class NostrChatRoomFeedViewModel: FeedViewModel(NostrChatRoomDataSource)
class NostrGlobalFeedViewModel: FeedViewModel(NostrGlobalDataSource)
class NostrThreadFeedViewModel: FeedViewModel(NostrThreadDataSource)
class NostrUserProfileFeedViewModel: FeedViewModel(NostrUserProfileDataSource)
2023-01-11 18:31:20 +00:00
2023-01-21 15:31:23 +00:00
class NostrChatroomListKnownFeedViewModel: FeedViewModel(NostrChatroomListDataSource) {
override fun newListFromDataSource(): List<Note> {
// Filter: all channels + PMs the account has replied to
return super.newListFromDataSource().filter {
val me = NostrChatroomListDataSource.account.userProfile()
it.channel != null || me.hasSentMessagesTo(it.author)
2023-01-21 15:31:23 +00:00
}
}
}
class NostrChatroomListNewFeedViewModel: FeedViewModel(NostrChatroomListDataSource) {
override fun newListFromDataSource(): List<Note> {
// Filter: no channels + PMs the account has never replied to
return super.newListFromDataSource().filter {
val me = NostrChatroomListDataSource.account.userProfile()
it.channel == null && !me.hasSentMessagesTo(it.author)
2023-01-21 15:31:23 +00:00
}
}
}
2023-01-18 23:59:18 +00:00
class NostrHomeFeedViewModel: FeedViewModel(NostrHomeDataSource) {
override fun newListFromDataSource(): List<Note> {
// Filter: no replies
return dataSource.feed().filter { it.isNewThread() }.take(100)
}
}
class NostrHomeRepliesFeedViewModel: FeedViewModel(NostrHomeDataSource) {
override fun newListFromDataSource(): List<Note> {
// Filter: only replies
return dataSource.feed().filter {! it.isNewThread() }.take(100)
}
}
2023-01-18 23:59:18 +00:00
abstract class FeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() {
2023-01-11 18:31:20 +00:00
private val _feedContent = MutableStateFlow<FeedState>(FeedState.Loading)
val feedContent = _feedContent.asStateFlow()
2023-01-21 15:31:23 +00:00
open fun newListFromDataSource(): List<Note> {
return dataSource.loadTop()
}
fun hardRefresh() {
dataSource.resetFilters()
}
2023-01-11 18:31:20 +00:00
fun refresh() {
viewModelScope.launch(Dispatchers.Default) {
2023-01-21 15:31:23 +00:00
val notes = newListFromDataSource()
val oldNotesState = feedContent.value
if (oldNotesState is FeedState.Loaded) {
if (notes != oldNotesState.feed) {
withContext(Dispatchers.Main) {
updateFeed(notes)
}
}
} else {
withContext(Dispatchers.Main) {
updateFeed(notes)
}
}
2023-01-11 18:31:20 +00:00
}
}
private fun updateFeed(notes: List<Note>) {
2023-01-27 22:28:59 +00:00
val currentState = feedContent.value
if (notes.isEmpty()) {
_feedContent.update { FeedState.Empty }
2023-01-27 22:28:59 +00:00
} else if (currentState is FeedState.Loaded) {
// updates the current list
currentState.feed.value = notes
} else {
2023-01-27 22:28:59 +00:00
_feedContent.update { FeedState.Loaded(mutableStateOf(notes)) }
2023-01-11 18:31:20 +00:00
}
}
private var handlerWaiting = AtomicBoolean()
@Synchronized
private fun invalidateData() {
if (handlerWaiting.get()) return
handlerWaiting.set(true)
val scope = CoroutineScope(Job() + Dispatchers.Default)
scope.launch {
delay(100)
refresh()
handlerWaiting.set(false)
}
}
2023-01-11 18:31:20 +00:00
private val cacheListener: (LocalCacheState) -> Unit = {
invalidateData()
2023-01-11 18:31:20 +00:00
}
init {
LocalCache.live.observeForever(cacheListener)
}
override fun onCleared() {
LocalCache.live.removeObserver(cacheListener)
dataSource.stop()
viewModelScope.cancel()
super.onCleared()
}
}