2023-01-11 18:31:20 +00:00
|
|
|
package com.vitorpamplona.amethyst.ui.screen
|
|
|
|
|
2023-01-23 23:54:56 +00:00
|
|
|
import android.util.Log
|
2023-01-27 22:28:59 +00:00
|
|
|
import androidx.compose.runtime.mutableStateOf
|
2023-01-26 01:33:37 +00:00
|
|
|
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
|
2023-01-25 02:28:34 +00:00
|
|
|
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
|
2023-01-19 13:41:48 +00:00
|
|
|
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
|
2023-01-19 22:56:22 +00:00
|
|
|
import kotlinx.coroutines.withContext
|
2023-01-25 02:28:34 +00:00
|
|
|
import nostr.postr.events.TextNoteEvent
|
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()
|
2023-01-27 17:50:20 +00:00
|
|
|
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()
|
2023-01-27 17:50:20 +00:00
|
|
|
it.channel == null && !me.hasSentMessagesTo(it.author)
|
2023-01-21 15:31:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-01-18 23:59:18 +00:00
|
|
|
|
2023-01-30 16:51:30 +00:00
|
|
|
fun isNewThread(note: Note): Boolean {
|
|
|
|
return note.event is RepostEvent || note.replyTo == null || note.replyTo?.size == 0
|
|
|
|
}
|
|
|
|
|
2023-01-25 02:28:34 +00:00
|
|
|
class NostrHomeFeedViewModel: FeedViewModel(NostrHomeDataSource) {
|
|
|
|
override fun newListFromDataSource(): List<Note> {
|
|
|
|
// Filter: no replies
|
2023-01-30 16:51:30 +00:00
|
|
|
return dataSource.feed().filter { isNewThread(it) }.take(100)
|
2023-01-25 02:28:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-30 16:51:30 +00:00
|
|
|
class NostrHomeRepliesFeedViewModel: FeedViewModel(NostrHomeDataSource) {
|
|
|
|
override fun newListFromDataSource(): List<Note> {
|
|
|
|
// Filter: only replies
|
|
|
|
return dataSource.feed().filter {!isNewThread(it) }.take(100)
|
|
|
|
}
|
|
|
|
}
|
2023-01-25 02:28:34 +00:00
|
|
|
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2023-01-21 16:37:57 +00:00
|
|
|
fun hardRefresh() {
|
|
|
|
dataSource.resetFilters()
|
|
|
|
}
|
|
|
|
|
2023-01-11 18:31:20 +00:00
|
|
|
fun refresh() {
|
2023-01-23 23:54:56 +00:00
|
|
|
viewModelScope.launch(Dispatchers.Default) {
|
2023-01-21 15:31:23 +00:00
|
|
|
val notes = newListFromDataSource()
|
2023-01-18 20:30:39 +00:00
|
|
|
|
2023-01-19 22:56:22 +00:00
|
|
|
val oldNotesState = feedContent.value
|
|
|
|
if (oldNotesState is FeedState.Loaded) {
|
|
|
|
if (notes != oldNotesState.feed) {
|
|
|
|
withContext(Dispatchers.Main) {
|
|
|
|
updateFeed(notes)
|
|
|
|
}
|
|
|
|
}
|
2023-01-19 13:41:48 +00:00
|
|
|
} else {
|
2023-01-19 22:56:22 +00:00
|
|
|
withContext(Dispatchers.Main) {
|
|
|
|
updateFeed(notes)
|
|
|
|
}
|
2023-01-19 13:41:48 +00:00
|
|
|
}
|
2023-01-11 18:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-19 22:56:22 +00:00
|
|
|
fun updateFeed(notes: List<Note>) {
|
2023-01-27 22:28:59 +00:00
|
|
|
val currentState = feedContent.value
|
|
|
|
|
2023-01-19 22:56:22 +00:00
|
|
|
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
|
2023-01-19 22:56:22 +00:00
|
|
|
} else {
|
2023-01-27 22:28:59 +00:00
|
|
|
_feedContent.update { FeedState.Loaded(mutableStateOf(notes)) }
|
2023-01-11 18:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 20:30:39 +00:00
|
|
|
var handlerWaiting = false
|
|
|
|
fun invalidateData() {
|
2023-01-23 23:54:56 +00:00
|
|
|
synchronized(handlerWaiting) {
|
|
|
|
if (handlerWaiting) return
|
2023-01-18 20:30:39 +00:00
|
|
|
|
2023-01-23 23:54:56 +00:00
|
|
|
handlerWaiting = true
|
|
|
|
val scope = CoroutineScope(Job() + Dispatchers.Default)
|
|
|
|
scope.launch {
|
|
|
|
delay(100)
|
|
|
|
refresh()
|
|
|
|
handlerWaiting = false
|
|
|
|
}
|
2023-01-19 13:41:48 +00:00
|
|
|
}
|
2023-01-18 20:30:39 +00:00
|
|
|
}
|
|
|
|
|
2023-01-11 18:31:20 +00:00
|
|
|
private val cacheListener: (LocalCacheState) -> Unit = {
|
2023-01-18 20:30:39 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|