Moving away from handlers and into coroutines.

pull/3/head
Vitor Pamplona 2023-01-19 08:41:48 -05:00
rodzic 9d52180550
commit 8771584deb
10 zmienionych plików z 116 dodań i 92 usunięć

Wyświetl plik

@ -13,6 +13,7 @@ import java.util.Collections
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import nostr.postr.events.Event
@ -99,17 +100,18 @@ class Note(val idHex: String) {
val live: NoteLiveData = NoteLiveData(this)
// Refreshes observers in batches.
val filterHandler = Handler(Looper.getMainLooper())
val scope = CoroutineScope(Job() + Dispatchers.Main)
var handlerWaiting = false
@Synchronized
fun invalidateData() {
if (handlerWaiting) return
handlerWaiting = true
filterHandler.postDelayed({
scope.launch {
delay(100)
live.refresh()
handlerWaiting = false
}, 100)
}
}
}
@ -120,18 +122,12 @@ class NoteLiveData(val note: Note): LiveData<NoteState>(NoteState(note)) {
override fun onActive() {
super.onActive()
val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch {
NostrSingleEventDataSource.add(note.idHex)
}
NostrSingleEventDataSource.add(note.idHex)
}
override fun onInactive() {
super.onInactive()
val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch {
NostrSingleEventDataSource.remove(note.idHex)
}
NostrSingleEventDataSource.remove(note.idHex)
}
}

Wyświetl plik

@ -22,7 +22,7 @@ object UrlCachedPreviewer {
return
}
val scope = CoroutineScope(Job() + Dispatchers.Main)
val scope = CoroutineScope(Job() + Dispatchers.IO)
scope.launch {
BahaUrlPreview(url, object : IUrlPreviewCallback {
override fun onComplete(urlInfo: UrlInfoItem) {

Wyświetl plik

@ -11,6 +11,7 @@ import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import nostr.postr.events.ContactListEvent
@ -112,18 +113,18 @@ class User(val pubkey: ByteArray) {
val live: UserLiveData = UserLiveData(this)
// Refreshes observers in batches.
val filterHandler = Handler(Looper.getMainLooper())
val scope = CoroutineScope(Job() + Dispatchers.Main)
var handlerWaiting = false
@Synchronized
fun invalidateData() {
if (handlerWaiting) return
handlerWaiting = true
filterHandler.postDelayed({
println("User Refresh")
scope.launch {
delay(100)
live.refresh()
handlerWaiting = false
}, 100)
}
}
}
@ -153,24 +154,18 @@ class UserMetadata {
}
class UserLiveData(val user: User): LiveData<UserState>(UserState(user)) {
val scope = CoroutineScope(Job() + Dispatchers.Main)
fun refresh() {
postValue(UserState(user))
}
override fun onActive() {
super.onActive()
scope.launch {
NostrSingleUserDataSource.add(user.pubkeyHex)
}
NostrSingleUserDataSource.add(user.pubkeyHex)
}
override fun onInactive() {
super.onInactive()
scope.launch {
NostrSingleUserDataSource.remove(user.pubkeyHex)
}
NostrSingleUserDataSource.remove(user.pubkeyHex)
}
}

Wyświetl plik

@ -41,7 +41,7 @@ object NostrAccountDataSource: NostrDataSource<Note>("AccountData") {
return JsonFilter(
kinds = listOf(MetadataEvent.kind),
authors = listOf(account.userProfile().pubkeyHex),
limit = 1
limit = 3
)
}

Wyświetl plik

@ -20,6 +20,7 @@ import kotlin.time.measureTimedValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import nostr.postr.events.ContactListEvent
import nostr.postr.events.DeletionEvent
@ -118,7 +119,7 @@ abstract class NostrDataSource<T>(val debugName: String) {
val returningList = feed().take(100)
// prepare previews
val scope = CoroutineScope(Job() + Dispatchers.Main)
val scope = CoroutineScope(Job() + Dispatchers.IO)
scope.launch {
loadPreviews(returningList)
}
@ -147,17 +148,18 @@ abstract class NostrDataSource<T>(val debugName: String) {
channelIds.remove(channel.id)
}
val filterHandler = Handler(Looper.getMainLooper())
val scope = CoroutineScope(Job() + Dispatchers.IO)
var handlerWaiting = false
@Synchronized
fun invalidateFilters() {
if (handlerWaiting) return
handlerWaiting = true
filterHandler.postDelayed({
scope.launch {
delay(200)
resetFilters()
handlerWaiting = false
}, 200)
}
}
fun resetFilters() {

Wyświetl plik

@ -16,7 +16,7 @@ object NostrSingleUserDataSource: NostrDataSource<Note>("SingleUserFeed") {
JsonFilter(
kinds = listOf(MetadataEvent.kind),
authors = listOf(it.substring(0, 8)),
limit = 1
limit = 10
)
}
}

Wyświetl plik

@ -49,7 +49,10 @@ fun DrawerContent(navController: NavHostController,
accountViewModel: AccountViewModel,
accountStateViewModel: AccountStateViewModel) {
val accountUserState by accountViewModel.userLiveData.observeAsState()
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val accountUserState by account.userProfile().live.observeAsState()
val accountUser = accountUserState?.user
Surface(
@ -64,14 +67,18 @@ fun DrawerContent(navController: NavHostController,
model = banner,
contentDescription = "Profile Image",
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth().height(150.dp)
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
)
} else {
Image(
painter = painterResource(R.drawable.profile_banner),
contentDescription = "Profile Banner",
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth().height(150.dp)
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
)
}
@ -106,12 +113,15 @@ fun DrawerContent(navController: NavHostController,
fun ProfileContent(accountUser: User?, modifier: Modifier = Modifier, scaffoldState: ScaffoldState, navController: NavController) {
val coroutineScope = rememberCoroutineScope()
println("AAA " + accountUser?.profilePicture())
Column(modifier = modifier) {
AsyncImage(
model = accountUser?.profilePicture() ?: "https://robohash.org/ohno.png",
contentDescription = "Profile Image",
modifier = Modifier
.width(100.dp).height(100.dp)
.width(100.dp)
.height(100.dp)
.clip(shape = CircleShape)
.border(3.dp, MaterialTheme.colors.background, CircleShape)
.background(MaterialTheme.colors.background)
@ -205,7 +215,8 @@ fun NavigationRow(navController: NavHostController, scaffoldState: ScaffoldState
})
) {
Row(
modifier = Modifier.fillMaxWidth()
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 15.dp, horizontal = 25.dp),
verticalAlignment = Alignment.CenterVertically
) {

Wyświetl plik

@ -14,6 +14,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
@ -26,24 +27,28 @@ class CardFeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() {
private var lastNotes: List<Note>? = null
fun refresh() {
val scope = CoroutineScope(Job() + Dispatchers.Main)
val scope = CoroutineScope(Job() + Dispatchers.IO)
scope.launch {
val notes = dataSource.loadTop()
refreshSuspended()
}
}
val lastNotesCopy = lastNotes
private fun refreshSuspended() {
val notes = dataSource.loadTop()
val oldNotesState = feedContent.value
if (lastNotesCopy != null && oldNotesState is CardFeedState.Loaded) {
val newCards = convertToCard(notes.minus(lastNotesCopy))
if (newCards.isNotEmpty()) {
lastNotes = notes
updateFeed((oldNotesState.feed + newCards).sortedBy { it.createdAt() }.reversed())
}
} else {
val cards = convertToCard(notes)
val lastNotesCopy = lastNotes
val oldNotesState = feedContent.value
if (lastNotesCopy != null && oldNotesState is CardFeedState.Loaded) {
val newCards = convertToCard(notes.minus(lastNotesCopy))
if (newCards.isNotEmpty()) {
lastNotes = notes
updateFeed(cards)
updateFeed((oldNotesState.feed + newCards).sortedBy { it.createdAt() }.reversed())
}
} else {
val cards = convertToCard(notes)
lastNotes = notes
updateFeed(cards)
}
}
@ -76,31 +81,28 @@ class CardFeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() {
}
fun updateFeed(notes: List<Card>) {
if (notes.isEmpty()) {
_feedContent.update { CardFeedState.Empty }
} else {
_feedContent.update { CardFeedState.Loaded(notes) }
val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch {
if (notes.isEmpty()) {
_feedContent.update { CardFeedState.Empty }
} else {
_feedContent.update { CardFeedState.Loaded(notes) }
}
}
}
fun refreshCurrentList() {
val state = feedContent.value
if (state is CardFeedState.Loaded) {
_feedContent.update { CardFeedState.Loaded(state.feed) }
}
}
val filterHandler = Handler(Looper.getMainLooper())
val scope = CoroutineScope(Job() + Dispatchers.IO)
var handlerWaiting = false
@Synchronized
fun invalidateData() {
if (handlerWaiting) return
handlerWaiting = true
filterHandler.postDelayed({
scope.launch {
delay(100)
refresh()
handlerWaiting = false
}, 100)
}
}
private val cacheListener: (LocalCacheState) -> Unit = {

Wyświetl plik

@ -23,6 +23,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
@ -42,26 +43,33 @@ abstract class FeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel()
val feedContent = _feedContent.asStateFlow()
fun refresh() {
val scope = CoroutineScope(Job() + Dispatchers.Main)
val scope = CoroutineScope(Job() + Dispatchers.IO)
scope.launch {
val notes = dataSource.loadTop()
refreshSuspended()
}
}
val oldNotesState = feedContent.value
if (oldNotesState is FeedState.Loaded) {
if (notes != oldNotesState.feed) {
updateFeed(notes)
}
} else {
private fun refreshSuspended() {
val notes = dataSource.loadTop()
val oldNotesState = feedContent.value
if (oldNotesState is FeedState.Loaded) {
if (notes != oldNotesState.feed) {
updateFeed(notes)
}
} else {
updateFeed(notes)
}
}
fun updateFeed(notes: List<Note>) {
if (notes.isEmpty()) {
_feedContent.update { FeedState.Empty }
} else {
_feedContent.update { FeedState.Loaded(notes) }
val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch {
if (notes.isEmpty()) {
_feedContent.update { FeedState.Empty }
} else {
_feedContent.update { FeedState.Loaded(notes) }
}
}
}
@ -72,17 +80,18 @@ abstract class FeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel()
}
}
val filterHandler = Handler(Looper.getMainLooper())
val scope = CoroutineScope(Job() + Dispatchers.IO)
var handlerWaiting = false
@Synchronized
fun invalidateData() {
if (handlerWaiting) return
handlerWaiting = true
filterHandler.postDelayed({
scope.launch {
delay(100)
refresh()
handlerWaiting = false
}, 100)
}
}
private val cacheListener: (LocalCacheState) -> Unit = {

Wyświetl plik

@ -14,6 +14,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
@ -32,26 +33,33 @@ open class UserFeedViewModel(val dataSource: NostrDataSource<User>): ViewModel()
val feedContent = _feedContent.asStateFlow()
fun refresh() {
val scope = CoroutineScope(Job() + Dispatchers.Main)
val scope = CoroutineScope(Job() + Dispatchers.IO)
scope.launch {
val notes = dataSource.loadTop()
refreshSuspended()
}
}
val oldNotesState = feedContent.value
if (oldNotesState is UserFeedState.Loaded) {
if (notes != oldNotesState.feed) {
updateFeed(notes)
}
} else {
private fun refreshSuspended() {
val notes = dataSource.loadTop()
val oldNotesState = feedContent.value
if (oldNotesState is UserFeedState.Loaded) {
if (notes != oldNotesState.feed) {
updateFeed(notes)
}
} else {
updateFeed(notes)
}
}
fun updateFeed(notes: List<User>) {
if (notes.isEmpty()) {
_feedContent.update { UserFeedState.Empty }
} else {
_feedContent.update { UserFeedState.Loaded(notes) }
val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch {
if (notes.isEmpty()) {
_feedContent.update { UserFeedState.Empty }
} else {
_feedContent.update { UserFeedState.Loaded(notes) }
}
}
}
@ -62,17 +70,18 @@ open class UserFeedViewModel(val dataSource: NostrDataSource<User>): ViewModel()
}
}
val filterHandler = Handler(Looper.getMainLooper())
val scope = CoroutineScope(Job() + Dispatchers.IO)
var handlerWaiting = false
@Synchronized
fun invalidateData() {
if (handlerWaiting) return
handlerWaiting = true
filterHandler.postDelayed({
scope.launch {
delay(100)
refresh()
handlerWaiting = false
}, 100)
}
}
private val cacheListener: (LocalCacheState) -> Unit = {