From 1465ce112c964bb1b9d8b1fde879a98b03177b9f Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 7 Sep 2023 11:11:10 -0400 Subject: [PATCH] Refines relay icon row compose --- .../vitorpamplona/amethyst/model/Account.kt | 4 ++ .../com/vitorpamplona/amethyst/model/Note.kt | 13 ++++ .../amethyst/ui/actions/NewMediaView.kt | 10 +-- .../amethyst/ui/actions/NewPostView.kt | 10 +-- .../ui/actions/RelaySelectionDialog.kt | 27 +++----- .../amethyst/ui/note/NoteCompose.kt | 11 ++-- .../amethyst/ui/note/RelayListBox.kt | 61 +++---------------- .../amethyst/ui/note/RelayListRow.kt | 26 +++----- .../ui/screen/loggedIn/VideoScreen.kt | 9 +-- 9 files changed, 59 insertions(+), 112 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 04851d456..2d4199ea6 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -1686,6 +1686,10 @@ class Account( .toTypedArray() } + fun activeWriteRelays(): List { + return (activeRelays() ?: convertLocalRelays()).filter { it.write } + } + fun reconnectIfRelaysHaveChanged() { val newRelaySet = activeRelays() ?: convertLocalRelays() if (!Client.isSameRelaySetConfig(newRelaySet)) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt index 63788310a..35ebae885 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt @@ -734,6 +734,12 @@ class NoteLiveSet(u: Note) { it.note.boosts.toImmutableList() }.distinctUntilChanged() + val relayInfo = innerRelays.map { + it.note.relays.map { + RelayBriefInfo(it) + }.toImmutableList() + } + fun isInUse(): Boolean { return metadata.hasObservers() || reactions.hasObservers() || @@ -812,3 +818,10 @@ class NoteLoadingLiveData(val note: Note, initialValue: Y?) : MediatorLiveDat @Immutable class NoteState(val note: Note) + +@Immutable +data class RelayBriefInfo( + val url: String, + val displayUrl: String = url.trim().removePrefix("wss://").removePrefix("ws://").removeSuffix("/").intern(), + val favIcon: String = "https://$displayUrl/favicon.ico".intern() +) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt index b4344132c..9a9900b2c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt @@ -79,13 +79,7 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac var showRelaysDialog by remember { mutableStateOf(false) } - var relayList = account.activeRelays()?.filter { - it.write - }?.map { - it - } ?: account.convertLocalRelays().filter { - it.write - } + var relayList = account.activeWriteRelays() Dialog( onDismissRequest = { onClose() }, @@ -101,7 +95,7 @@ fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, ac ) { if (showRelaysDialog) { RelaySelectionDialog( - list = relayList, + preSelectedList = relayList, onClose = { showRelaysDialog = false }, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index fc43a4293..5e6bdacb5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -133,13 +133,7 @@ fun NewPostView( var showRelaysDialog by remember { mutableStateOf(false) } - var relayList = account.activeRelays()?.filter { - it.write - }?.map { - it - } ?: account.convertLocalRelays().filter { - it.write - } + var relayList = account.activeWriteRelays() LaunchedEffect(Unit) { postViewModel.load(account, baseReplyTo, quote) @@ -169,7 +163,7 @@ fun NewPostView( ) { if (showRelaysDialog) { RelaySelectionDialog( - list = relayList, + preSelectedList = relayList, onClose = { showRelaysDialog = false }, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt index e2d4af042..b979dc2fe 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.model.RelayInformation import com.vitorpamplona.amethyst.service.relays.Relay import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel @@ -36,12 +37,13 @@ import kotlinx.coroutines.launch data class RelayList( val relay: Relay, + val relayInfo: RelayBriefInfo, val isSelected: Boolean ) @Composable fun RelaySelectionDialog( - list: List, + preSelectedList: List, onClose: () -> Unit, onPost: (list: List) -> Unit, accountViewModel: AccountViewModel, @@ -49,20 +51,14 @@ fun RelaySelectionDialog( ) { val scope = rememberCoroutineScope() val context = LocalContext.current - val relayList = accountViewModel.account.activeRelays()?.filter { - it.write - }?.map { - it - } ?: accountViewModel.account.convertLocalRelays().filter { - it.write - } var relays by remember { mutableStateOf( - relayList.map { + accountViewModel.account.activeWriteRelays().map { RelayList( - it, - list.any { relay -> it.url == relay.url } + relay = it, + relayInfo = RelayBriefInfo(it.url), + isSelected = preSelectedList.any { relay -> it.url == relay.url } ) } ) @@ -115,14 +111,14 @@ fun RelaySelectionDialog( } ) - PostButton( + SaveButton( onPost = { val selectedRelays = relays.filter { it.isSelected } if (selectedRelays.isEmpty()) { scope.launch { Toast.makeText(context, context.getString(R.string.select_a_relay_to_continue), Toast.LENGTH_SHORT).show() } - return@PostButton + return@SaveButton } onPost(selectedRelays.map { it.relay }) onClose() @@ -153,10 +149,7 @@ fun RelaySelectionDialog( key = { _, item -> item.relay.url } ) { index, item -> RelaySwitch( - text = item.relay.url - .removePrefix("ws://") - .removePrefix("wss://") - .removeSuffix("/"), + text = item.relayInfo.displayUrl, checked = item.isSelected, onClick = { relays = relays.mapIndexed { j, item -> diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 66aaa30c1..0ce393707 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -82,6 +82,7 @@ import com.vitorpamplona.amethyst.model.Channel import com.vitorpamplona.amethyst.model.ConnectivityType import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.OnlineChecker import com.vitorpamplona.amethyst.service.ReverseGeoLocationUtil @@ -1624,9 +1625,9 @@ fun DisplayRelaySet( ) { val noteEvent = baseNote.event as? RelaySetEvent ?: return - val relays by remember { - mutableStateOf>( - noteEvent.relays().toImmutableList() + val relays by remember(baseNote) { + mutableStateOf( + noteEvent.relays().map { RelayBriefInfo(it) }.toImmutableList() ) } @@ -1681,7 +1682,7 @@ fun DisplayRelaySet( toMembersShow.forEach { relay -> Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = CenterVertically) { Text( - relay.trim().removePrefix("wss://").removePrefix("ws://").removeSuffix("/"), + text = relay.displayUrl, fontWeight = FontWeight.Bold, maxLines = 1, overflow = TextOverflow.Ellipsis, @@ -1691,7 +1692,7 @@ fun DisplayRelaySet( ) Column(modifier = Modifier.padding(start = 10.dp)) { - RelayOptionsAction(relay, accountViewModel, nav) + RelayOptionsAction(relay.url, accountViewModel, nav) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListBox.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListBox.kt index 1796f8171..58bac1021 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListBox.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListBox.kt @@ -11,17 +11,16 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.ui.screen.equalImmutableLists +import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonBoxModifer @@ -29,80 +28,40 @@ import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonIconButtonModifie import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonIconModifier import com.vitorpamplona.amethyst.ui.theme.placeholderText import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch @Composable public fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { var expanded by remember { mutableStateOf(false) } - var showShowMore by remember { mutableStateOf(false) } - var lazyRelayList by remember { - val baseNumber = baseNote.relays.map { - it.removePrefix("wss://").removePrefix("ws://") - }.toImmutableList() - - mutableStateOf(baseNumber) - } - var shortRelayList by remember { - mutableStateOf(lazyRelayList.take(3).toImmutableList()) - } - - val scope = rememberCoroutineScope() - - WatchRelayLists(baseNote) { relayList -> - if (!equalImmutableLists(relayList, lazyRelayList)) { - scope.launch(Dispatchers.Main) { - lazyRelayList = relayList - shortRelayList = relayList.take(3).toImmutableList() - } - } - - val nextShowMore = relayList.size > 3 - if (nextShowMore != showShowMore) { - scope.launch(Dispatchers.Main) { - // only triggers recomposition when actually different - showShowMore = nextShowMore - } + val relayList by baseNote.live().relayInfo.observeAsState(persistentListOf()) + val shortRelayList by remember { + derivedStateOf { + relayList.take(3).toImmutableList() } } Spacer(DoubleVertSpacer) if (expanded) { - VerticalRelayPanelWithFlow(lazyRelayList, accountViewModel, nav) + VerticalRelayPanelWithFlow(relayList, accountViewModel, nav) } else { VerticalRelayPanelWithFlow(shortRelayList, accountViewModel, nav) } - if (showShowMore && !expanded) { + if (relayList.size > 3 && !expanded) { ShowMoreRelaysButton { expanded = true } } } -@Composable -private fun WatchRelayLists(baseNote: Note, onListChanges: (ImmutableList) -> Unit) { - val noteRelaysState by baseNote.live().relays.observeAsState() - - LaunchedEffect(key1 = noteRelaysState) { - launch(Dispatchers.IO) { - val relayList = noteRelaysState?.note?.relays?.map { - it.removePrefix("wss://").removePrefix("ws://") - } ?: emptyList() - - onListChanges(relayList.toImmutableList()) - } - } -} - @OptIn(ExperimentalLayoutApi::class) @Composable @Stable private fun VerticalRelayPanelWithFlow( - relays: ImmutableList, + relays: ImmutableList, accountViewModel: AccountViewModel, nav: (String) -> Unit ) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt index 4c738c091..8c3736791 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt @@ -17,7 +17,6 @@ import androidx.compose.material.icons.filled.ChevronRight import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf @@ -33,6 +32,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.map import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.model.RelayBriefInfo import com.vitorpamplona.amethyst.model.RelayInformation import com.vitorpamplona.amethyst.ui.actions.RelayInformationDialog import com.vitorpamplona.amethyst.ui.actions.loadRelayInfo @@ -44,6 +44,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size15Modifier import com.vitorpamplona.amethyst.ui.theme.Size15dp import com.vitorpamplona.amethyst.ui.theme.StdStartPadding import com.vitorpamplona.amethyst.ui.theme.placeholderText +import kotlinx.collections.immutable.toImmutableList @Composable public fun RelayBadgesHorizontal(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { @@ -59,15 +60,13 @@ public fun RelayBadgesHorizontal(baseNote: Note, accountViewModel: AccountViewMo @OptIn(ExperimentalLayoutApi::class) @Composable fun RenderRelayList(baseNote: Note, expanded: MutableState, accountViewModel: AccountViewModel, nav: (String) -> Unit) { - val noteRelays by baseNote.live().relays.map { - it.note.relays - }.observeAsState(baseNote.relays) + val noteRelays by baseNote.live().relayInfo.observeAsState() FlowRow(StdStartPadding) { val relaysToDisplay = remember(noteRelays, expanded.value) { - if (expanded.value) noteRelays else noteRelays.take(3) + if (expanded.value) noteRelays else noteRelays?.take(3)?.toImmutableList() } - relaysToDisplay.forEach { + relaysToDisplay?.forEach { RenderRelay(it, accountViewModel, nav) } } @@ -104,14 +103,7 @@ fun ChatRelayExpandButton(onClick: () -> Unit) { } @Composable -fun RenderRelay(dirtyUrl: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) { - val iconUrl by remember(dirtyUrl) { - derivedStateOf { - val cleanUrl = dirtyUrl.trim().removePrefix("wss://").removePrefix("ws://").removeSuffix("/") - "https://$cleanUrl/favicon.ico" - } - } - +fun RenderRelay(relay: RelayBriefInfo, accountViewModel: AccountViewModel, nav: (String) -> Unit) { var relayInfo: RelayInformation? by remember { mutableStateOf(null) } if (relayInfo != null) { @@ -130,7 +122,7 @@ fun RenderRelay(dirtyUrl: String, accountViewModel: AccountViewModel, nav: (Stri val interactionSource = remember { MutableInteractionSource() } val ripple = rememberRipple(bounded = false, radius = Size15dp) - val clickableModifier = remember(dirtyUrl) { + val clickableModifier = remember(relay) { Modifier .padding(1.dp) .size(Size15dp) @@ -139,7 +131,7 @@ fun RenderRelay(dirtyUrl: String, accountViewModel: AccountViewModel, nav: (Stri interactionSource = interactionSource, indication = ripple, onClick = { - loadRelayInfo(dirtyUrl, context, scope) { + loadRelayInfo(relay.url, context, scope) { relayInfo = it } } @@ -149,7 +141,7 @@ fun RenderRelay(dirtyUrl: String, accountViewModel: AccountViewModel, nav: (Stri Box( modifier = clickableModifier ) { - RenderRelayIcon(iconUrl) + RenderRelayIcon(relay.favIcon) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt index 615b06c11..9073c78ad 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt @@ -416,14 +416,11 @@ private fun VideoUserOptionAction( @OptIn(ExperimentalLayoutApi::class) @Composable private fun RelayBadges(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { - val noteRelaysState by baseNote.live().relays.observeAsState() - val noteRelays = remember(noteRelaysState) { - noteRelaysState?.note?.relays ?: emptySet() - } + val noteRelays by baseNote.live().relayInfo.observeAsState() FlowRow() { - noteRelays.forEach { dirtyUrl -> - RenderRelay(dirtyUrl, accountViewModel, nav) + noteRelays?.forEach { relayInfo -> + RenderRelay(relayInfo, accountViewModel, nav) } } }