From db740f4747f8b5c2e098b8051129e3fb377d8135 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Tue, 5 Sep 2023 17:21:36 -0400 Subject: [PATCH] Upgrades the RelayPoolStatus to a Flow --- .../amethyst/service/relays/RelayPool.kt | 40 +++++++++------ .../amethyst/ui/navigation/DrawerContent.kt | 50 +++++++++++-------- .../amethyst/ui/screen/RelayPoolViewModel.kt | 20 -------- 3 files changed, 55 insertions(+), 55 deletions(-) delete mode 100644 app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RelayPoolViewModel.kt diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/relays/RelayPool.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/relays/RelayPool.kt index 3233307e9..7638d7b45 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/relays/RelayPool.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/relays/RelayPool.kt @@ -1,9 +1,13 @@ package com.vitorpamplona.amethyst.service.relays -import androidx.lifecycle.LiveData +import androidx.compose.runtime.Immutable import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.EventInterface +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow /** * RelayPool manages the connection to multiple Relays and lets consumers deal with simple events. @@ -12,6 +16,11 @@ object RelayPool : Relay.Listener { private var relays = listOf() private var listeners = setOf() + // Backing property to avoid flow emissions from other classes + private var _lastStatus = RelayPoolStatus(0, 0) + private val _statusFlow = MutableSharedFlow(1, 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + val statusFlow: SharedFlow = _statusFlow.asSharedFlow() + fun availableRelays(): Int { return relays.size } @@ -76,11 +85,13 @@ object RelayPool : Relay.Listener { fun addRelay(relay: Relay) { relay.register(this) relays += relay + updateStatus() } fun removeRelay(relay: Relay) { relay.unregister(this) relays = relays.minus(relay) + updateStatus() } fun register(listener: Listener) { @@ -109,12 +120,14 @@ object RelayPool : Relay.Listener { override fun onError(relay: Relay, subscriptionId: String, error: Error) { listeners.forEach { it.onError(error, subscriptionId, relay) } - refreshObservers() + updateStatus() } override fun onRelayStateChange(relay: Relay, type: Relay.Type, channel: String?) { listeners.forEach { it.onRelayStateChange(type, relay, channel) } - refreshObservers() + if (type != Relay.Type.EOSE) { + updateStatus() + } } override fun onSendResponse(relay: Relay, eventId: String, success: Boolean, message: String) { @@ -125,18 +138,15 @@ object RelayPool : Relay.Listener { listeners.forEach { it.onAuth(relay, challenge) } } - // Observers line up here. - val live: RelayPoolLiveData = RelayPoolLiveData(this) - - private fun refreshObservers() { - live.refresh() + private fun updateStatus() { + val connected = connectedRelays() + val available = availableRelays() + if (_lastStatus.connected != connected || _lastStatus.available != available) { + _lastStatus = RelayPoolStatus(connected, available) + _statusFlow.tryEmit(_lastStatus) + } } } -class RelayPoolLiveData(val relays: RelayPool) : LiveData(RelayPoolState(relays)) { - fun refresh() { - postValue(RelayPoolState(relays)) - } -} - -class RelayPoolState(val relays: RelayPool) +@Immutable +data class RelayPoolStatus(val connected: Int, val available: Int, val isConnected: Boolean = connected > 0) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt index 0e8f37c8d..c567d0481 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt @@ -40,6 +40,7 @@ import androidx.compose.material.icons.filled.Send import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -63,7 +64,6 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.map -import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import com.vitorpamplona.amethyst.BuildConfig import com.vitorpamplona.amethyst.LocalPreferences @@ -72,11 +72,12 @@ import com.vitorpamplona.amethyst.ServiceManager import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.HttpClient +import com.vitorpamplona.amethyst.service.relays.RelayPool +import com.vitorpamplona.amethyst.service.relays.RelayPoolStatus import com.vitorpamplona.amethyst.ui.actions.NewRelayListView import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy import com.vitorpamplona.amethyst.ui.note.LoadStatuses -import com.vitorpamplona.amethyst.ui.screen.RelayPoolViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog @@ -454,7 +455,6 @@ fun ListContent( } val coroutineScope = rememberCoroutineScope() - val relayViewModel: RelayPoolViewModel = viewModel { RelayPoolViewModel() } var wantsToEditRelays by remember { mutableStateOf(false) } @@ -490,7 +490,7 @@ fun ListContent( ) IconRowRelays( - relayViewModel = relayViewModel, + accountViewModel = accountViewModel, onClick = { coroutineScope.launch { scaffoldState.drawerState.close() @@ -633,27 +633,37 @@ private fun enableTor( } @Composable -private fun RelayStatus( - relayViewModel: RelayPoolViewModel -) { - val connectedRelaysText by relayViewModel.connectionStatus.observeAsState("--/--") - val isConnected by relayViewModel.isConnected.observeAsState(false) +private fun RelayStatus(accountViewModel: AccountViewModel) { + val connectedRelaysText by RelayPool.statusFlow.collectAsState(initial = RelayPoolStatus(0, 0)) - RenderRelayStatus(connectedRelaysText, isConnected) + RenderRelayStatus(connectedRelaysText) } @Composable private fun RenderRelayStatus( - connectedRelaysText: String, - isConnected: Boolean + relayPool: RelayPoolStatus ) { + val text by remember(relayPool) { + derivedStateOf { + "${relayPool.connected}/${relayPool.available}" + } + } + + val placeHolder = MaterialTheme.colors.placeholderText + + val color by remember(relayPool) { + derivedStateOf { + if (relayPool.isConnected) { + placeHolder + } else { + Color.Red + } + } + } + Text( - text = connectedRelaysText, - color = if (isConnected) { - MaterialTheme.colors.placeholderText - } else { - Color.Red - }, + text = text, + color = color, style = MaterialTheme.typography.subtitle1 ) } @@ -709,7 +719,7 @@ fun IconRow(title: String, icon: Int, tint: Color, onClick: () -> Unit, onLongCl } @Composable -fun IconRowRelays(relayViewModel: RelayPoolViewModel, onClick: () -> Unit) { +fun IconRowRelays(accountViewModel: AccountViewModel, onClick: () -> Unit) { Row( modifier = Modifier .fillMaxWidth() @@ -736,7 +746,7 @@ fun IconRowRelays(relayViewModel: RelayPoolViewModel, onClick: () -> Unit) { Spacer(modifier = Modifier.width(Size16dp)) - RelayStatus(relayViewModel = relayViewModel) + RelayStatus(accountViewModel = accountViewModel) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RelayPoolViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RelayPoolViewModel.kt deleted file mode 100644 index a57974b6c..000000000 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/RelayPoolViewModel.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.vitorpamplona.amethyst.ui.screen - -import androidx.compose.runtime.Stable -import androidx.lifecycle.ViewModel -import androidx.lifecycle.distinctUntilChanged -import androidx.lifecycle.map -import com.vitorpamplona.amethyst.service.relays.RelayPool - -@Stable -class RelayPoolViewModel : ViewModel() { - val connectionStatus = RelayPool.live.map { - val connectedRelays = it.relays.connectedRelays() - val availableRelays = it.relays.availableRelays() - "$connectedRelays/$availableRelays" - }.distinctUntilChanged() - - val isConnected = RelayPool.live.map { - it.relays.connectedRelays() > 0 - }.distinctUntilChanged() -}