Removes the forced search relay selection and helps users setup their relay list for search.
This commit is contained in:
@@ -229,32 +229,69 @@ class Account(
|
||||
|
||||
val connectToRelaysFlow =
|
||||
combineTransform(
|
||||
getNIP65RelayListFlow(),
|
||||
getDMRelayListFlow(),
|
||||
getSearchRelayListFlow(),
|
||||
userProfile().flow().relays.stateFlow,
|
||||
) { dmRelayList, userProfile ->
|
||||
val newRelaySet = activeRelays() ?: convertLocalRelays()
|
||||
val newDMRelaySet = (dmRelayList.note.event as? ChatMessageRelayListEvent)?.relays()
|
||||
) { nip65RelayList, dmRelayList, searchRelayList, userProfile ->
|
||||
val baseRelaySet = activeRelays() ?: convertLocalRelays()
|
||||
val newDMRelaySet = (dmRelayList.note.event as? ChatMessageRelayListEvent)?.relays()?.toSet() ?: emptySet()
|
||||
val searchRelaySet = (dmRelayList.note.event as? SearchRelayListEvent)?.relays()?.toSet() ?: Constants.defaultSearchRelaySet
|
||||
val nip65RelaySet = (dmRelayList.note.event as? AdvertisedRelayListEvent)?.relays()
|
||||
|
||||
if (newDMRelaySet == null) {
|
||||
emit(newRelaySet)
|
||||
} else {
|
||||
var mappedRelaySet =
|
||||
newRelaySet.map {
|
||||
if (newDMRelaySet?.contains(it.url) == true) {
|
||||
Relay(it.url, true, true, it.activeTypes + FeedType.PRIVATE_DMS)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
newDMRelaySet.forEach { newUrl ->
|
||||
if (mappedRelaySet.filter { it.url == newUrl }.isEmpty()) {
|
||||
mappedRelaySet = mappedRelaySet + Relay(newUrl, true, true, setOf(FeedType.PRIVATE_DMS))
|
||||
var mappedRelaySet =
|
||||
baseRelaySet.map {
|
||||
if (newDMRelaySet.contains(it.url) == true) {
|
||||
Relay(it.url, true, true, it.activeTypes + FeedType.PRIVATE_DMS)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
emit(mappedRelaySet.toTypedArray())
|
||||
newDMRelaySet.forEach { newUrl ->
|
||||
if (mappedRelaySet.filter { it.url == newUrl }.isEmpty()) {
|
||||
mappedRelaySet = mappedRelaySet + Relay(newUrl, true, true, setOf(FeedType.PRIVATE_DMS))
|
||||
}
|
||||
}
|
||||
|
||||
mappedRelaySet =
|
||||
mappedRelaySet.map {
|
||||
if (searchRelaySet.contains(it.url) == true) {
|
||||
Relay(it.url, true, true, it.activeTypes + FeedType.PRIVATE_DMS)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
searchRelaySet.forEach { newUrl ->
|
||||
if (mappedRelaySet.filter { it.url == newUrl }.isEmpty()) {
|
||||
mappedRelaySet = mappedRelaySet + Relay(newUrl, true, true, setOf(FeedType.SEARCH))
|
||||
}
|
||||
}
|
||||
|
||||
mappedRelaySet =
|
||||
mappedRelaySet.map { relay ->
|
||||
val nip65setup = nip65RelaySet?.firstOrNull { relay.url == it.relayUrl }
|
||||
if (nip65setup != null) {
|
||||
val read = nip65setup.type == AdvertisedRelayListEvent.AdvertisedRelayType.BOTH || nip65setup.type == AdvertisedRelayListEvent.AdvertisedRelayType.READ
|
||||
val write = nip65setup.type == AdvertisedRelayListEvent.AdvertisedRelayType.BOTH || nip65setup.type == AdvertisedRelayListEvent.AdvertisedRelayType.READ
|
||||
|
||||
Relay(relay.url, read, write, relay.activeTypes)
|
||||
} else {
|
||||
relay
|
||||
}
|
||||
}
|
||||
|
||||
nip65RelaySet?.forEach { newNip65Setup ->
|
||||
if (mappedRelaySet.filter { it.url == newNip65Setup.relayUrl }.isEmpty()) {
|
||||
val read = newNip65Setup.type == AdvertisedRelayListEvent.AdvertisedRelayType.BOTH || newNip65Setup.type == AdvertisedRelayListEvent.AdvertisedRelayType.READ
|
||||
val write = newNip65Setup.type == AdvertisedRelayListEvent.AdvertisedRelayType.BOTH || newNip65Setup.type == AdvertisedRelayListEvent.AdvertisedRelayType.READ
|
||||
|
||||
mappedRelaySet = mappedRelaySet + Relay(newNip65Setup.relayUrl, read, write, setOf(FeedType.FOLLOWS, FeedType.PUBLIC_CHATS))
|
||||
}
|
||||
}
|
||||
|
||||
emit(mappedRelaySet.toTypedArray())
|
||||
}
|
||||
|
||||
val connectToRelays = connectToRelaysFlow.stateIn(scope, SharingStarted.Eagerly, activeRelays() ?: convertLocalRelays())
|
||||
@@ -2463,7 +2500,7 @@ class Account(
|
||||
|
||||
// Takes a User's relay list and adds the types of feeds they are active for.
|
||||
fun activeRelays(): Array<Relay>? {
|
||||
var usersRelayList =
|
||||
val usersRelayList =
|
||||
userProfile().latestContactList?.relays()?.map {
|
||||
val localFeedTypes =
|
||||
localRelays.firstOrNull { localRelay -> localRelay.url == it.key }?.feedTypes
|
||||
@@ -2476,24 +2513,6 @@ class Account(
|
||||
Relay(it.key, it.value.read, it.value.write, localFeedTypes)
|
||||
} ?: return null
|
||||
|
||||
// Ugly, but forces nostr.band as the only search-supporting relay today.
|
||||
// TODO: Remove when search becomes more available.
|
||||
val searchRelays =
|
||||
usersRelayList.filter { it.url.removeSuffix("/") in Constants.forcedRelaysForSearchSet }
|
||||
val hasSearchRelay = usersRelayList.any { it.activeTypes.contains(FeedType.SEARCH) }
|
||||
if (!hasSearchRelay && searchRelays.isEmpty()) {
|
||||
usersRelayList =
|
||||
usersRelayList +
|
||||
Constants.forcedRelayForSearch.map {
|
||||
Relay(
|
||||
it.url,
|
||||
it.read,
|
||||
it.write,
|
||||
it.feedTypes,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return usersRelayList.toTypedArray()
|
||||
}
|
||||
|
||||
|
||||
@@ -52,11 +52,5 @@ object Constants {
|
||||
RelaySetupInfo("wss://relay.noswhere.com", read = true, write = false, feedTypes = activeTypesSearch),
|
||||
)
|
||||
|
||||
val forcedRelayForSearch =
|
||||
arrayOf(
|
||||
RelaySetupInfo("wss://relay.nostr.band", read = true, write = false, feedTypes = activeTypesSearch),
|
||||
RelaySetupInfo("wss://nostr.wine", read = true, write = false, feedTypes = activeTypesSearch),
|
||||
RelaySetupInfo("wss://relay.noswhere.com", read = true, write = false, feedTypes = activeTypesSearch),
|
||||
)
|
||||
val forcedRelaysForSearchSet = forcedRelayForSearch.map { it.url }
|
||||
val defaultSearchRelaySet = setOf("wss://relay.nostr.band", "wss://nostr.wine", "wss://relay.noswhere.com")
|
||||
}
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.actions.relays
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AddSearchRelayListDialog(
|
||||
onClose: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
val postViewModel: SearchRelayListViewModel = viewModel()
|
||||
|
||||
LaunchedEffect(Unit) { postViewModel.load(accountViewModel.account) }
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = onClose,
|
||||
properties = DialogProperties(usePlatformDefaultWidth = false),
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
|
||||
Text(stringResource(R.string.search_relays_title))
|
||||
|
||||
SaveButton(
|
||||
onPost = {
|
||||
postViewModel.create()
|
||||
onClose()
|
||||
},
|
||||
true,
|
||||
)
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
CloseButton(
|
||||
onPress = {
|
||||
postViewModel.clear()
|
||||
onClose()
|
||||
},
|
||||
)
|
||||
},
|
||||
colors =
|
||||
TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
),
|
||||
)
|
||||
},
|
||||
) { pad ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
16.dp,
|
||||
pad.calculateTopPadding(),
|
||||
16.dp,
|
||||
pad.calculateBottomPadding(),
|
||||
),
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
) {
|
||||
Explanation()
|
||||
|
||||
SearchRelayList(postViewModel, accountViewModel, onClose, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Explanation() {
|
||||
Card(modifier = MaterialTheme.colorScheme.imageModifier) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.search_relays_not_found_editing),
|
||||
)
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.search_relays_not_found_examples),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ fun ResetSearchRelays(postViewModel: SearchRelayListViewModel) {
|
||||
Button(
|
||||
onClick = {
|
||||
postViewModel.deleteAll()
|
||||
Constants.forcedRelayForSearch.forEach { postViewModel.addRelay(BasicRelaySetupInfo(it.url)) }
|
||||
Constants.defaultSearchRelaySet.forEach { postViewModel.addRelay(BasicRelaySetupInfo(it)) }
|
||||
postViewModel.loadRelayDocuments()
|
||||
},
|
||||
) {
|
||||
|
||||
@@ -40,7 +40,6 @@ import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Groups
|
||||
import androidx.compose.material.icons.filled.Paid
|
||||
import androidx.compose.material.icons.filled.Public
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material.icons.filled.SyncProblem
|
||||
import androidx.compose.material.icons.filled.Upload
|
||||
import androidx.compose.material3.Button
|
||||
@@ -310,7 +309,6 @@ fun ClickableRelayItem(
|
||||
onTogglePrivateDMs = onTogglePrivateDMs,
|
||||
onTogglePublicChats = onTogglePublicChats,
|
||||
onToggleGlobal = onToggleGlobal,
|
||||
onToggleSearch = onToggleSearch,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -492,7 +490,6 @@ private fun ActiveToggles(
|
||||
onTogglePrivateDMs: (RelaySetupInfo) -> Unit,
|
||||
onTogglePublicChats: (RelaySetupInfo) -> Unit,
|
||||
onToggleGlobal: (RelaySetupInfo) -> Unit,
|
||||
onToggleSearch: (RelaySetupInfo) -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val context = LocalContext.current
|
||||
@@ -638,40 +635,6 @@ private fun ActiveToggles(
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(
|
||||
modifier = Size30Modifier,
|
||||
onClick = { onToggleSearch(item) },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
stringResource(R.string.search_feed),
|
||||
modifier =
|
||||
Modifier.padding(horizontal = 5.dp)
|
||||
.size(15.dp)
|
||||
.combinedClickable(
|
||||
onClick = { onToggleSearch(item) },
|
||||
onLongClick = {
|
||||
scope.launch {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.search_feed),
|
||||
Toast.LENGTH_SHORT,
|
||||
)
|
||||
.show()
|
||||
}
|
||||
},
|
||||
),
|
||||
tint =
|
||||
if (item.feedTypes.contains(FeedType.SEARCH)) {
|
||||
MaterialTheme.colorScheme.allGoodColor
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(
|
||||
alpha = 0.32f,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
-26
@@ -28,7 +28,6 @@ import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
||||
import com.vitorpamplona.amethyst.service.relays.Constants
|
||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
||||
import com.vitorpamplona.amethyst.service.relays.RelayPool
|
||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||
import kotlinx.collections.immutable.toImmutableSet
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -76,31 +75,6 @@ class Kind3RelayListViewModel : ViewModel() {
|
||||
var relayFile = account.userProfile().latestContactList?.relays()
|
||||
|
||||
if (relayFile != null) {
|
||||
// Ugly, but forces nostr.band as the only search-supporting relay today.
|
||||
// TODO: Remove when search becomes more available.
|
||||
|
||||
val needsSearchRelay =
|
||||
relayFile.none { it.key.removeSuffix("/") in Constants.forcedRelaysForSearchSet } &&
|
||||
relayFile.none {
|
||||
account.localRelays
|
||||
.filter { localRelay -> localRelay.url == it.key }
|
||||
.firstOrNull()
|
||||
?.feedTypes
|
||||
?.contains(FeedType.SEARCH)
|
||||
?: false
|
||||
}
|
||||
|
||||
if (needsSearchRelay) {
|
||||
relayFile =
|
||||
relayFile +
|
||||
Constants.forcedRelayForSearch.map {
|
||||
Pair(
|
||||
it.url,
|
||||
ContactListEvent.ReadWrite(it.read, it.write),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
relayFile
|
||||
.map {
|
||||
val liveRelay = RelayPool.getRelay(it.key)
|
||||
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.note.elements
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.ThemeType
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.AddSearchRelayListDialog
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.BigPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||
import com.vitorpamplona.quartz.crypto.KeyPair
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.events.SearchRelayListEvent
|
||||
import fr.acinq.secp256k1.Hex
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun AddInboxRelayForSearchCardPreview() {
|
||||
val sharedPreferencesViewModel: SharedPreferencesViewModel = viewModel()
|
||||
val myCoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
sharedPreferencesViewModel.init()
|
||||
sharedPreferencesViewModel.updateTheme(ThemeType.DARK)
|
||||
|
||||
val pubkey = "989c3734c46abac7ce3ce229971581a5a6ee39cdd6aa7261a55823fa7f8c4799"
|
||||
|
||||
val myAccount =
|
||||
Account(
|
||||
keyPair =
|
||||
KeyPair(
|
||||
privKey = Hex.decode("0f761f8a5a481e26f06605a1d9b3e9eba7a107d351f43c43a57469b788274499"),
|
||||
pubKey = Hex.decode(pubkey),
|
||||
forcePubKeyCheck = false,
|
||||
),
|
||||
scope = myCoroutineScope,
|
||||
)
|
||||
|
||||
val accountViewModel =
|
||||
AccountViewModel(
|
||||
myAccount,
|
||||
sharedPreferencesViewModel.sharedPrefs,
|
||||
)
|
||||
|
||||
ThemeComparisonColumn {
|
||||
AddInboxRelayForSearchCard(
|
||||
accountViewModel = accountViewModel,
|
||||
nav = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ObserveRelayListForSearchAndDisplayIfNotFound(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
ObserveRelayListForSearch(
|
||||
accountViewModel = accountViewModel,
|
||||
) { relayListEvent ->
|
||||
if (relayListEvent == null || relayListEvent.relays().isEmpty()) {
|
||||
AddInboxRelayForSearchCard(
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ObserveRelayListForSearch(
|
||||
accountViewModel: AccountViewModel,
|
||||
inner: @Composable (relayListEvent: SearchRelayListEvent?) -> Unit,
|
||||
) {
|
||||
ObserveRelayListForSearch(
|
||||
pubkey = accountViewModel.account.userProfile().pubkeyHex,
|
||||
accountViewModel = accountViewModel,
|
||||
) { relayListEvent ->
|
||||
inner(relayListEvent)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ObserveRelayListForSearch(
|
||||
pubkey: HexKey,
|
||||
accountViewModel: AccountViewModel,
|
||||
inner: @Composable (relayListEvent: SearchRelayListEvent?) -> Unit,
|
||||
) {
|
||||
LoadAddressableNote(
|
||||
SearchRelayListEvent.createAddressTag(pubkey),
|
||||
accountViewModel,
|
||||
) { relayList ->
|
||||
if (relayList != null) {
|
||||
val relayListNoteState by relayList.live().metadata.observeAsState()
|
||||
val relayListEvent = relayListNoteState?.note?.event as? SearchRelayListEvent
|
||||
|
||||
inner(relayListEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddInboxRelayForSearchCard(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
Column(modifier = StdPadding) {
|
||||
Card(
|
||||
modifier = MaterialTheme.colorScheme.imageModifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = BigPadding,
|
||||
) {
|
||||
// Title
|
||||
Text(
|
||||
text = stringResource(id = R.string.search_relays_not_found),
|
||||
style =
|
||||
TextStyle(
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
),
|
||||
)
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.search_relays_not_found_description),
|
||||
)
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
||||
var wantsToEditRelays by remember { mutableStateOf(false) }
|
||||
if (wantsToEditRelays) {
|
||||
AddSearchRelayListDialog({ wantsToEditRelays = false }, accountViewModel, nav = nav)
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
wantsToEditRelays = true
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.dm_relays_not_found_create_now))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,7 @@ import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.SearchIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.ObserveRelayListForSearchAndDisplayIfNotFound
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
@@ -154,6 +155,7 @@ fun SearchScreen(
|
||||
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
SearchBar(searchBarViewModel, listState)
|
||||
ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav)
|
||||
DisplaySearchResults(searchBarViewModel, listState, nav, accountViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user