mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-04-09 20:39:24 +02:00
Adds communities, hashtags and geohashes to the lists on the top navigation bar.
This commit is contained in:
parent
dc314a94ff
commit
3efa3a68fe
@ -2478,13 +2478,7 @@ class Account(
|
||||
val privKey = keyPair.privKey
|
||||
|
||||
return if (listName != null) {
|
||||
val aTag = ATag(
|
||||
PeopleListEvent.kind,
|
||||
userProfile().pubkeyHex,
|
||||
listName,
|
||||
null
|
||||
).toTag()
|
||||
val list = LocalCache.addressables[aTag]
|
||||
val list = LocalCache.addressables[listName]
|
||||
if (list != null) {
|
||||
val publicHexList = (list.event as? PeopleListEvent)?.bookmarkedPeople() ?: emptySet()
|
||||
val privateHexList = privKey?.let {
|
||||
@ -2507,13 +2501,7 @@ class Account(
|
||||
val privKey = keyPair.privKey
|
||||
|
||||
return if (listName != null) {
|
||||
val aTag = ATag(
|
||||
PeopleListEvent.kind,
|
||||
userProfile().pubkeyHex,
|
||||
listName,
|
||||
null
|
||||
).toTag()
|
||||
val list = LocalCache.addressables[aTag]
|
||||
val list = LocalCache.addressables[listName]
|
||||
if (list != null) {
|
||||
val publicAddresses = list.event?.hashtags() ?: emptySet()
|
||||
val privateAddresses = privKey?.let {
|
||||
@ -2536,13 +2524,7 @@ class Account(
|
||||
val privKey = keyPair.privKey
|
||||
|
||||
return if (listName != null) {
|
||||
val aTag = ATag(
|
||||
PeopleListEvent.kind,
|
||||
userProfile().pubkeyHex,
|
||||
listName,
|
||||
null
|
||||
).toTag()
|
||||
val list = LocalCache.addressables[aTag]
|
||||
val list = LocalCache.addressables[listName]
|
||||
if (list != null) {
|
||||
val publicAddresses = list.event?.geohashes() ?: emptySet()
|
||||
val privateAddresses = privKey?.let {
|
||||
@ -2565,13 +2547,7 @@ class Account(
|
||||
val privKey = keyPair.privKey
|
||||
|
||||
return if (listName != null) {
|
||||
val aTag = ATag(
|
||||
PeopleListEvent.kind,
|
||||
userProfile().pubkeyHex,
|
||||
listName,
|
||||
null
|
||||
).toTag()
|
||||
val list = LocalCache.addressables[aTag]
|
||||
val list = LocalCache.addressables[listName]
|
||||
if (list != null) {
|
||||
val publicAddresses = list.event?.taggedAddresses()?.map { it.toTag() } ?: emptySet()
|
||||
val privateAddresses = privKey?.let {
|
||||
|
@ -43,6 +43,10 @@ class AddressableNote(val address: ATag) : Note(address.toTag()) {
|
||||
|
||||
return minOf(publishedAt, lastCreatedAt)
|
||||
}
|
||||
|
||||
fun dTag(): String? {
|
||||
return (event as? AddressableEvent)?.dTag()
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
|
@ -319,7 +319,7 @@ class User(val pubkeyHex: String) {
|
||||
return latestContactList?.verifiedFollowKeySet ?: emptySet()
|
||||
}
|
||||
|
||||
fun cachedFollowingTagSet(): Set<HexKey> {
|
||||
fun cachedFollowingTagSet(): Set<String> {
|
||||
return latestContactList?.verifiedFollowTagSet ?: emptySet()
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@ import com.vitorpamplona.amethyst.model.ServersAvailable
|
||||
import com.vitorpamplona.amethyst.ui.components.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -186,8 +187,7 @@ fun ImageVideoPost(postViewModel: NewMediaModel, accountViewModel: AccountViewMo
|
||||
Triple(ServersAvailable.NIP95, stringResource(id = R.string.upload_server_relays_nip95), stringResource(id = R.string.upload_server_relays_nip95_explainer))
|
||||
)
|
||||
|
||||
val fileServerOptions = remember { fileServers.map { it.second }.toImmutableList() }
|
||||
val fileServerExplainers = remember { fileServers.map { it.third }.toImmutableList() }
|
||||
val fileServerOptions = remember { fileServers.map { TitleExplainer(it.second, it.third) }.toImmutableList() }
|
||||
val resolver = LocalContext.current.contentResolver
|
||||
|
||||
Row(
|
||||
@ -252,7 +252,6 @@ fun ImageVideoPost(postViewModel: NewMediaModel, accountViewModel: AccountViewMo
|
||||
label = stringResource(id = R.string.file_server),
|
||||
placeholder = fileServers.firstOrNull { it.first == accountViewModel.account.defaultFileServer }?.second ?: fileServers[0].second,
|
||||
options = fileServerOptions,
|
||||
explainers = fileServerExplainers,
|
||||
onSelect = {
|
||||
postViewModel.selectedServer = fileServers[it].first
|
||||
},
|
||||
|
@ -93,6 +93,7 @@ import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.MyTextField
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.UserLine
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
@ -1318,8 +1319,7 @@ fun ImageVideoDescription(
|
||||
Triple(ServersAvailable.NIP95, stringResource(id = R.string.upload_server_relays_nip95), stringResource(id = R.string.upload_server_relays_nip95_explainer))
|
||||
)
|
||||
|
||||
val fileServerOptions = remember { fileServers.map { it.second }.toImmutableList() }
|
||||
val fileServerExplainers = remember { fileServers.map { it.third }.toImmutableList() }
|
||||
val fileServerOptions = remember { fileServers.map { TitleExplainer(it.second, it.third) }.toImmutableList() }
|
||||
|
||||
var selectedServer by remember { mutableStateOf(defaultServer) }
|
||||
var message by remember { mutableStateOf("") }
|
||||
@ -1433,7 +1433,6 @@ fun ImageVideoDescription(
|
||||
label = stringResource(id = R.string.file_server),
|
||||
placeholder = fileServers.filter { it.first == defaultServer }.firstOrNull()?.second ?: fileServers[0].second,
|
||||
options = fileServerOptions,
|
||||
explainers = fileServerExplainers,
|
||||
onSelect = {
|
||||
selectedServer = fileServers[it].first
|
||||
},
|
||||
|
@ -20,6 +20,7 @@ import androidx.compose.material.OutlinedTextField
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@ -37,8 +38,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
fun TextSpinner(
|
||||
label: String?,
|
||||
placeholder: String,
|
||||
options: ImmutableList<String>,
|
||||
explainers: ImmutableList<String>? = null,
|
||||
options: ImmutableList<TitleExplainer>,
|
||||
onSelect: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
@ -74,8 +74,8 @@ fun TextSpinner(
|
||||
|
||||
if (optionsShowing) {
|
||||
options.isNotEmpty().also {
|
||||
SpinnerSelectionDialog(options = options, explainers = explainers, onDismiss = { optionsShowing = false }) {
|
||||
currentText = options[it]
|
||||
SpinnerSelectionDialog(options = options, onDismiss = { optionsShowing = false }) {
|
||||
currentText = options[it].title
|
||||
optionsShowing = false
|
||||
onSelect(it)
|
||||
}
|
||||
@ -85,10 +85,40 @@ fun TextSpinner(
|
||||
|
||||
@Composable
|
||||
fun SpinnerSelectionDialog(
|
||||
options: ImmutableList<String>,
|
||||
explainers: ImmutableList<String>?,
|
||||
options: ImmutableList<TitleExplainer>,
|
||||
onDismiss: () -> Unit,
|
||||
onSelect: (Int) -> Unit
|
||||
) {
|
||||
SpinnerSelectionDialog(
|
||||
options = options,
|
||||
onSelect = onSelect,
|
||||
onDismiss = onDismiss
|
||||
) { item ->
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = item.title, color = MaterialTheme.colors.onSurface)
|
||||
}
|
||||
item.explainer?.let {
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(text = it, color = Color.Gray, fontSize = Font14SP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> SpinnerSelectionDialog(
|
||||
options: ImmutableList<T>,
|
||||
onSelect: (Int) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
onRenderItem: @Composable (T) -> Unit
|
||||
) {
|
||||
Dialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
@ -106,23 +136,7 @@ fun SpinnerSelectionDialog(
|
||||
}
|
||||
) {
|
||||
Column() {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(text = item, color = MaterialTheme.colors.onSurface)
|
||||
}
|
||||
explainers?.getOrNull(index)?.let {
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(text = it, color = Color.Gray, fontSize = Font14SP)
|
||||
}
|
||||
}
|
||||
onRenderItem(item)
|
||||
}
|
||||
}
|
||||
if (index < options.lastIndex) {
|
||||
@ -133,3 +147,6 @@ fun SpinnerSelectionDialog(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class TitleExplainer(val title: String, val explainer: String? = null)
|
||||
|
@ -33,6 +33,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@ -54,11 +55,13 @@ import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.map
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import coil.Coil
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.GLOBAL_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.KIND3_FOLLOWS
|
||||
import com.vitorpamplona.amethyst.model.LiveActivitiesChannel
|
||||
@ -88,6 +91,7 @@ import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadChannel
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadCityName
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadUser
|
||||
import com.vitorpamplona.amethyst.ui.note.LongCommunityHeader
|
||||
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
||||
@ -117,6 +121,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size34dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
import com.vitorpamplona.quartz.events.ContactListEvent
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
@ -382,11 +387,12 @@ fun StoriesTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState
|
||||
val list by accountViewModel.storiesListLiveData.observeAsState(GLOBAL_FOLLOWS)
|
||||
|
||||
FollowList(
|
||||
followLists,
|
||||
list,
|
||||
true
|
||||
followListsModel = followLists,
|
||||
listName = list,
|
||||
withGlobal = true,
|
||||
withRoutes = false
|
||||
) { listName ->
|
||||
accountViewModel.account.changeDefaultStoriesFollowList(listName)
|
||||
accountViewModel.account.changeDefaultStoriesFollowList(listName.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -397,11 +403,16 @@ fun HomeTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldState, a
|
||||
val list by accountViewModel.homeListLiveData.observeAsState(KIND3_FOLLOWS)
|
||||
|
||||
FollowList(
|
||||
followLists,
|
||||
list,
|
||||
true
|
||||
followListsModel = followLists,
|
||||
listName = list,
|
||||
withGlobal = true,
|
||||
withRoutes = true
|
||||
) { listName ->
|
||||
accountViewModel.account.changeDefaultHomeFollowList(listName)
|
||||
if (listName.type == CodeNameType.ROUTE) {
|
||||
nav(listName.code)
|
||||
} else {
|
||||
accountViewModel.account.changeDefaultHomeFollowList(listName.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -412,11 +423,12 @@ fun NotificationTopBar(followLists: FollowListViewModel, scaffoldState: Scaffold
|
||||
val list by accountViewModel.notificationListLiveData.observeAsState(GLOBAL_FOLLOWS)
|
||||
|
||||
FollowList(
|
||||
followLists,
|
||||
list,
|
||||
true
|
||||
followListsModel = followLists,
|
||||
listName = list,
|
||||
withGlobal = true,
|
||||
withRoutes = false
|
||||
) { listName ->
|
||||
accountViewModel.account.changeDefaultNotificationFollowList(listName)
|
||||
accountViewModel.account.changeDefaultNotificationFollowList(listName.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -427,11 +439,12 @@ fun DiscoveryTopBar(followLists: FollowListViewModel, scaffoldState: ScaffoldSta
|
||||
val list by accountViewModel.discoveryListLiveData.observeAsState(GLOBAL_FOLLOWS)
|
||||
|
||||
FollowList(
|
||||
followLists,
|
||||
list,
|
||||
true
|
||||
followListsModel = followLists,
|
||||
listName = list,
|
||||
withGlobal = true,
|
||||
withRoutes = false
|
||||
) { listName ->
|
||||
accountViewModel.account.changeDefaultDiscoveryFollowList(listName)
|
||||
accountViewModel.account.changeDefaultDiscoveryFollowList(listName.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -523,37 +536,81 @@ private fun LoggedInUserPictureDrawer(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FollowList(followListsModel: FollowListViewModel, listName: String, withGlobal: Boolean, onChange: (String) -> Unit) {
|
||||
val kind3Follow = Pair(KIND3_FOLLOWS, stringResource(id = R.string.follow_list_kind3follows))
|
||||
val globalFollow = Pair(GLOBAL_FOLLOWS, stringResource(id = R.string.follow_list_global))
|
||||
fun FollowList(
|
||||
followListsModel: FollowListViewModel,
|
||||
listName: String,
|
||||
withGlobal: Boolean,
|
||||
withRoutes: Boolean,
|
||||
onChange: (CodeName) -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val kind3Follow = CodeName(KIND3_FOLLOWS, ResourceName(R.string.follow_list_kind3follows, context), CodeNameType.HARDCODED)
|
||||
val globalFollow = CodeName(GLOBAL_FOLLOWS, ResourceName(R.string.follow_list_global, context), CodeNameType.HARDCODED)
|
||||
|
||||
val defaultOptions = if (withGlobal) listOf(kind3Follow, globalFollow) else listOf(kind3Follow)
|
||||
|
||||
val followLists by followListsModel.followLists.collectAsState()
|
||||
val followLists by followListsModel.peopleLists.collectAsState()
|
||||
val routeList by followListsModel.routes.collectAsState()
|
||||
|
||||
val allLists = remember(followLists) {
|
||||
(defaultOptions + followLists)
|
||||
if (withRoutes) {
|
||||
(defaultOptions + followLists + routeList)
|
||||
} else {
|
||||
(defaultOptions + followLists)
|
||||
}
|
||||
}
|
||||
|
||||
val followNames by remember(followLists) {
|
||||
derivedStateOf {
|
||||
allLists.map { it.second }.toImmutableList()
|
||||
allLists.map { it.name }.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTextSpinner(
|
||||
placeholder = allLists.firstOrNull { it.first == listName }?.second ?: "Select an Option",
|
||||
placeholder = allLists.firstOrNull { it.code == listName }?.name?.name() ?: "Select an Option",
|
||||
options = followNames,
|
||||
onSelect = {
|
||||
onChange(allLists.getOrNull(it)?.first ?: KIND3_FOLLOWS)
|
||||
onChange(allLists.getOrNull(it) ?: kind3Follow)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
enum class CodeNameType {
|
||||
HARDCODED, PEOPLE_LIST, ROUTE
|
||||
}
|
||||
|
||||
abstract class Name {
|
||||
abstract fun name(): String
|
||||
}
|
||||
|
||||
class GeoHashName(val geoHashTag: String) : Name() {
|
||||
override fun name() = "/g/$geoHashTag"
|
||||
}
|
||||
class HashtagName(val hashTag: String) : Name() {
|
||||
override fun name() = "#$hashTag"
|
||||
}
|
||||
class ResourceName(val resourceId: Int, val context: Context) : Name() {
|
||||
override fun name() = context.getString(resourceId)
|
||||
}
|
||||
|
||||
class PeopleListName(val note: AddressableNote) : Name() {
|
||||
override fun name() = note.dTag() ?: ""
|
||||
}
|
||||
class CommunityName(val note: AddressableNote) : Name() {
|
||||
override fun name() = "/n/${(note.dTag() ?: "")}"
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class CodeName(val code: String, val name: Name, val type: CodeNameType)
|
||||
|
||||
@Stable
|
||||
class FollowListViewModel(val account: Account) : ViewModel() {
|
||||
private var _followLists = MutableStateFlow<ImmutableList<Pair<String, String>>>(emptyList<Pair<String, String>>().toPersistentList())
|
||||
val followLists = _followLists.asStateFlow()
|
||||
private var _peopleLists = MutableStateFlow<ImmutableList<CodeName>>(emptyList<CodeName>().toPersistentList())
|
||||
val peopleLists = _peopleLists.asStateFlow()
|
||||
|
||||
private var _routes = MutableStateFlow<ImmutableList<CodeName>>(emptyList<CodeName>().toPersistentList())
|
||||
val routes = _routes.asStateFlow()
|
||||
|
||||
fun refresh() {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
@ -571,14 +628,34 @@ class FollowListViewModel(val account: Account) : ViewModel() {
|
||||
event.pubKey == account.userProfile().pubkeyHex &&
|
||||
(event.tags.size > 1 || event.content.length > 50)
|
||||
) {
|
||||
Pair(event.dTag(), event.dTag())
|
||||
CodeName(event.address().toTag(), PeopleListName(it.value), CodeNameType.PEOPLE_LIST)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.sortedBy { it.second }.toImmutableList()
|
||||
}.sortedBy { it.name.name() }.toImmutableList()
|
||||
|
||||
if (!equalImmutableLists(_followLists.value, newFollowLists)) {
|
||||
_followLists.emit(newFollowLists)
|
||||
if (!equalImmutableLists(_peopleLists.value, newFollowLists)) {
|
||||
_peopleLists.emit(newFollowLists)
|
||||
}
|
||||
|
||||
val communities = account.userProfile().cachedFollowingCommunitiesSet().mapNotNull {
|
||||
LocalCache.checkGetOrCreateAddressableNote(it)?.let { communityNote ->
|
||||
CodeName("Community/${communityNote.idHex}", CommunityName(communityNote), CodeNameType.ROUTE)
|
||||
}
|
||||
}
|
||||
|
||||
val hashtags = account.userProfile().cachedFollowingTagSet().map {
|
||||
CodeName("Hashtag/$it", HashtagName(it), CodeNameType.ROUTE)
|
||||
}
|
||||
|
||||
val geotags = account.userProfile().cachedFollowingGeohashSet().map {
|
||||
CodeName("Geohash/$it", GeoHashName(it), CodeNameType.ROUTE)
|
||||
}
|
||||
|
||||
val routeList = (communities + hashtags + geotags).sortedBy { it.name.name() }.toImmutableList()
|
||||
|
||||
if (!equalImmutableLists(_routes.value, routeList)) {
|
||||
_routes.emit(routeList)
|
||||
}
|
||||
}
|
||||
|
||||
@ -590,7 +667,10 @@ class FollowListViewModel(val account: Account) : ViewModel() {
|
||||
collectorJob = viewModelScope.launch(Dispatchers.IO) {
|
||||
LocalCache.live.newEventBundles.collect { newNotes ->
|
||||
checkNotInMainThread()
|
||||
if (newNotes.any { it.event is PeopleListEvent }) {
|
||||
if (newNotes.any {
|
||||
it.event?.pubKey() == account.userProfile().pubkeyHex && (it.event is PeopleListEvent || it.event is ContactListEvent)
|
||||
}
|
||||
) {
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
@ -612,8 +692,7 @@ class FollowListViewModel(val account: Account) : ViewModel() {
|
||||
@Composable
|
||||
fun SimpleTextSpinner(
|
||||
placeholder: String,
|
||||
options: ImmutableList<String>,
|
||||
explainers: ImmutableList<String>? = null,
|
||||
options: ImmutableList<Name>,
|
||||
onSelect: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
@ -649,10 +728,68 @@ fun SimpleTextSpinner(
|
||||
|
||||
if (optionsShowing) {
|
||||
options.isNotEmpty().also {
|
||||
SpinnerSelectionDialog(options = options, explainers = explainers, onDismiss = { optionsShowing = false }) {
|
||||
currentText = options[it]
|
||||
optionsShowing = false
|
||||
onSelect(it)
|
||||
SpinnerSelectionDialog(
|
||||
options = options,
|
||||
onDismiss = { optionsShowing = false },
|
||||
onSelect = {
|
||||
currentText = options[it].name()
|
||||
optionsShowing = false
|
||||
onSelect(it)
|
||||
}
|
||||
) {
|
||||
RenderOption(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderOption(it: Name) {
|
||||
when (it) {
|
||||
is GeoHashName -> {
|
||||
LoadCityName(it.geoHashTag) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = "/g/$it", color = MaterialTheme.colors.onSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
is HashtagName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = it.name(), color = MaterialTheme.colors.onSurface)
|
||||
}
|
||||
}
|
||||
is ResourceName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(id = it.resourceId), color = MaterialTheme.colors.onSurface)
|
||||
}
|
||||
}
|
||||
is PeopleListName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = it.name(), color = MaterialTheme.colors.onSurface)
|
||||
}
|
||||
}
|
||||
is CommunityName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
val name by it.note.live().metadata.map {
|
||||
"/n/" + (it.note as? AddressableNote)?.dTag()
|
||||
}.observeAsState()
|
||||
|
||||
Text(text = name ?: "", color = MaterialTheme.colors.onSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2579,7 +2579,7 @@ fun LoadStatuses(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplayLocation(geohash: String, nav: (String) -> Unit) {
|
||||
fun LoadCityName(geohash: String, content: @Composable (String) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
var cityName by remember(geohash) {
|
||||
mutableStateOf<String>(geohash)
|
||||
@ -2594,18 +2594,25 @@ fun DisplayLocation(geohash: String, nav: (String) -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
ClickableText(
|
||||
text = AnnotatedString(cityName),
|
||||
onClick = { nav("Geohash/$geohash") },
|
||||
style = LocalTextStyle.current.copy(
|
||||
color = MaterialTheme.colors.primary.copy(
|
||||
alpha = 0.52f
|
||||
content(cityName)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DisplayLocation(geohash: String, nav: (String) -> Unit) {
|
||||
LoadCityName(geohash) { cityName ->
|
||||
ClickableText(
|
||||
text = AnnotatedString(cityName),
|
||||
onClick = { nav("Geohash/$geohash") },
|
||||
style = LocalTextStyle.current.copy(
|
||||
color = MaterialTheme.colors.primary.copy(
|
||||
alpha = 0.52f
|
||||
),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.Bold
|
||||
),
|
||||
fontSize = Font14SP,
|
||||
fontWeight = FontWeight.Bold
|
||||
),
|
||||
maxLines = 1
|
||||
)
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -89,7 +89,6 @@ import com.vitorpamplona.amethyst.ui.theme.DarkerGreen
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfDoubleVertSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.Height4dpModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.ModifierWidth3dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.NoSoTinyBorders
|
||||
import com.vitorpamplona.amethyst.ui.theme.ReactionRowExpandButton
|
||||
@ -135,13 +134,14 @@ fun ReactionsRow(
|
||||
|
||||
InnerReactionRow(baseNote, showReactionDetail, wantsToSeeReactions, accountViewModel, nav)
|
||||
|
||||
Spacer(modifier = HalfDoubleVertSpacer)
|
||||
|
||||
LoadAndDisplayZapraiser(baseNote, showReactionDetail, wantsToSeeReactions, accountViewModel)
|
||||
|
||||
if (showReactionDetail && wantsToSeeReactions.value) {
|
||||
ReactionDetailGallery(baseNote, nav, accountViewModel)
|
||||
Spacer(modifier = HalfDoubleVertSpacer)
|
||||
}
|
||||
|
||||
Spacer(modifier = HalfDoubleVertSpacer)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -251,7 +251,6 @@ private fun LoadAndDisplayZapraiser(
|
||||
}
|
||||
|
||||
if (zapraiserAmount > 0) {
|
||||
Spacer(modifier = Height4dpModifier)
|
||||
Box(
|
||||
modifier = remember {
|
||||
ReactionRowZapraiserSize
|
||||
|
@ -73,6 +73,7 @@ import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.qrcode.SimpleQrCodeScanner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.getFragmentActivity
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
||||
@ -219,8 +220,7 @@ fun UpdateZapAmountDialog(
|
||||
Triple(LnZapEvent.ZapType.NONZAP, stringResource(id = R.string.zap_type_nonzap), stringResource(id = R.string.zap_type_nonzap_explainer))
|
||||
)
|
||||
|
||||
val zapOptions = remember { zapTypes.map { it.second }.toImmutableList() }
|
||||
val zapOptionExplainers = remember { zapTypes.map { it.third }.toImmutableList() }
|
||||
val zapOptions = remember { zapTypes.map { TitleExplainer(it.second, it.third) }.toImmutableList() }
|
||||
|
||||
LaunchedEffect(accountViewModel, nip47uri) {
|
||||
postViewModel.load()
|
||||
@ -361,7 +361,6 @@ fun UpdateZapAmountDialog(
|
||||
placeholder = zapTypes.filter { it.first == accountViewModel.defaultZapType() }
|
||||
.first().second,
|
||||
options = zapOptions,
|
||||
explainers = zapOptionExplainers,
|
||||
onSelect = {
|
||||
postViewModel.selectedZapType = zapTypes[it].first
|
||||
},
|
||||
|
@ -47,6 +47,7 @@ import com.vitorpamplona.amethyst.service.ZapPaymentHandler
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
@ -107,8 +108,7 @@ fun ZapCustomDialog(
|
||||
Triple(LnZapEvent.ZapType.NONZAP, stringResource(id = R.string.zap_type_nonzap), stringResource(id = R.string.zap_type_nonzap_explainer))
|
||||
)
|
||||
|
||||
val zapOptions = remember { zapTypes.map { it.second }.toImmutableList() }
|
||||
val zapOptionExplainers = remember { zapTypes.map { it.third }.toImmutableList() }
|
||||
val zapOptions = remember { zapTypes.map { TitleExplainer(it.second, it.third) }.toImmutableList() }
|
||||
var selectedZapType by remember(accountViewModel) { mutableStateOf(accountViewModel.account.defaultZapType) }
|
||||
|
||||
Dialog(
|
||||
@ -181,7 +181,6 @@ fun ZapCustomDialog(
|
||||
label = stringResource(id = R.string.zap_type),
|
||||
placeholder = zapTypes.filter { it.first == accountViewModel.account.defaultZapType }.first().second,
|
||||
options = zapOptions,
|
||||
explainers = zapOptionExplainers,
|
||||
onSelect = {
|
||||
selectedZapType = zapTypes[it].first
|
||||
},
|
||||
|
@ -56,7 +56,7 @@ fun ReportNoteDialog(note: Note, accountViewModel: AccountViewModel, onDismiss:
|
||||
Pair(ReportEvent.ReportType.ILLEGAL, stringResource(R.string.report_dialog_illegal))
|
||||
)
|
||||
|
||||
val reasonOptions = remember { reportTypes.map { it.second }.toImmutableList() }
|
||||
val reasonOptions = remember { reportTypes.map { TitleExplainer(it.second) }.toImmutableList() }
|
||||
var additionalReason by remember { mutableStateOf("") }
|
||||
var selectedReason by remember { mutableStateOf(-1) }
|
||||
|
||||
|
@ -111,15 +111,15 @@ fun SettingsScreen(
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val selectedItens = persistentListOf(
|
||||
stringResource(ConnectivityType.ALWAYS.reourceId),
|
||||
stringResource(ConnectivityType.WIFI_ONLY.reourceId),
|
||||
stringResource(ConnectivityType.NEVER.reourceId)
|
||||
TitleExplainer(stringResource(ConnectivityType.ALWAYS.reourceId)),
|
||||
TitleExplainer(stringResource(ConnectivityType.WIFI_ONLY.reourceId)),
|
||||
TitleExplainer(stringResource(ConnectivityType.NEVER.reourceId))
|
||||
)
|
||||
|
||||
val themeItens = persistentListOf(
|
||||
stringResource(R.string.system),
|
||||
stringResource(R.string.light),
|
||||
stringResource(R.string.dark)
|
||||
TitleExplainer(stringResource(R.string.system)),
|
||||
TitleExplainer(stringResource(R.string.light)),
|
||||
TitleExplainer(stringResource(R.string.dark))
|
||||
)
|
||||
|
||||
val settings = accountViewModel.account.settings
|
||||
@ -132,7 +132,7 @@ fun SettingsScreen(
|
||||
val context = LocalContext.current
|
||||
|
||||
val languageEntries = context.getLangPreferenceDropdownEntries()
|
||||
val languageList = languageEntries.keys.toImmutableList()
|
||||
val languageList = languageEntries.keys.map { TitleExplainer(it) }.toImmutableList()
|
||||
val languageIndex = getLanguageIndex(languageEntries)
|
||||
|
||||
Column(
|
||||
@ -149,12 +149,12 @@ fun SettingsScreen(
|
||||
) {
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
val job = scope.launch(Dispatchers.IO) {
|
||||
val locale = languageEntries[languageList[it]]
|
||||
val locale = languageEntries[languageList[it].title]
|
||||
accountViewModel.account.settings.preferredLanguage = locale
|
||||
LocalPreferences.saveToEncryptedStorage(accountViewModel.account)
|
||||
}
|
||||
job.join()
|
||||
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(languageEntries[languageList[it]])
|
||||
val appLocale: LocaleListCompat = LocaleListCompat.forLanguageTags(languageEntries[languageList[it].title])
|
||||
AppCompatDelegate.setApplicationLocales(appLocale)
|
||||
}
|
||||
}
|
||||
@ -227,7 +227,7 @@ fun SettingsScreen(
|
||||
fun SettingsRow(
|
||||
name: Int,
|
||||
description: Int,
|
||||
selectedItens: ImmutableList<String>,
|
||||
selectedItens: ImmutableList<TitleExplainer>,
|
||||
selectedIndex: Int,
|
||||
onSelect: (Int) -> Unit
|
||||
) {
|
||||
@ -255,7 +255,7 @@ fun SettingsRow(
|
||||
|
||||
TextSpinner(
|
||||
label = "",
|
||||
placeholder = selectedItens[selectedIndex],
|
||||
placeholder = selectedItens[selectedIndex].title,
|
||||
options = selectedItens,
|
||||
onSelect = onSelect,
|
||||
modifier = Modifier
|
||||
|
Loading…
x
Reference in New Issue
Block a user