mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-06-28 20:00:55 +02:00
User Relay information in Profiles
This commit is contained in:
parent
3c52ff6e8d
commit
d94f35de0e
@ -98,13 +98,14 @@ object LocalCache {
|
|||||||
fun consume(event: TextNoteEvent, relay: Relay? = null) {
|
fun consume(event: TextNoteEvent, relay: Relay? = null) {
|
||||||
val note = getOrCreateNote(event.id.toHex())
|
val note = getOrCreateNote(event.id.toHex())
|
||||||
|
|
||||||
|
val author = getOrCreateUser(event.pubKey)
|
||||||
|
|
||||||
if (relay != null)
|
if (relay != null)
|
||||||
note.addRelay(relay)
|
author.addRelay(relay, event.createdAt)
|
||||||
|
|
||||||
// Already processed this event.
|
// Already processed this event.
|
||||||
if (note.event != null) return
|
if (note.event != null) return
|
||||||
|
|
||||||
val author = getOrCreateUser(event.pubKey)
|
|
||||||
val mentions = Collections.synchronizedList(event.mentions.map { getOrCreateUser(decodePublicKey(it)) })
|
val mentions = Collections.synchronizedList(event.mentions.map { getOrCreateUser(decodePublicKey(it)) })
|
||||||
val replyTo = Collections.synchronizedList(event.replyTos.map { getOrCreateNote(it) }.toMutableList())
|
val replyTo = Collections.synchronizedList(event.replyTos.map { getOrCreateNote(it) }.toMutableList())
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package com.vitorpamplona.amethyst.model
|
package com.vitorpamplona.amethyst.model
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
||||||
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||||
@ -10,7 +8,6 @@ import java.time.Instant
|
|||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import java.util.Date
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@ -40,8 +37,6 @@ class Note(val idHex: String) {
|
|||||||
|
|
||||||
var channel: Channel? = null
|
var channel: Channel? = null
|
||||||
|
|
||||||
val relays = Collections.synchronizedSet(mutableSetOf<Relay>())
|
|
||||||
|
|
||||||
fun loadEvent(event: Event, author: User, mentions: List<User>, replyTo: MutableList<Note>) {
|
fun loadEvent(event: Event, author: User, mentions: List<User>, replyTo: MutableList<Note>) {
|
||||||
this.event = event
|
this.event = event
|
||||||
this.author = author
|
this.author = author
|
||||||
@ -93,11 +88,6 @@ class Note(val idHex: String) {
|
|||||||
invalidateData(liveReactions)
|
invalidateData(liveReactions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addRelay(relay: Relay) {
|
|
||||||
if (relays.add(relay))
|
|
||||||
invalidateData(liveRelays)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addReport(note: Note) {
|
fun addReport(note: Note) {
|
||||||
if (reports.add(note))
|
if (reports.add(note))
|
||||||
invalidateData(liveReports)
|
invalidateData(liveReports)
|
||||||
@ -134,7 +124,6 @@ class Note(val idHex: String) {
|
|||||||
val liveBoosts: NoteLiveData = NoteLiveData(this)
|
val liveBoosts: NoteLiveData = NoteLiveData(this)
|
||||||
val liveReplies: NoteLiveData = NoteLiveData(this)
|
val liveReplies: NoteLiveData = NoteLiveData(this)
|
||||||
val liveReports: NoteLiveData = NoteLiveData(this)
|
val liveReports: NoteLiveData = NoteLiveData(this)
|
||||||
val liveRelays: NoteLiveData = NoteLiveData(this)
|
|
||||||
|
|
||||||
// Refreshes observers in batches.
|
// Refreshes observers in batches.
|
||||||
var handlerWaiting = false
|
var handlerWaiting = false
|
||||||
|
@ -40,6 +40,7 @@ class User(val pubkey: ByteArray) {
|
|||||||
val messages = ConcurrentHashMap<User, MutableSet<Note>>()
|
val messages = ConcurrentHashMap<User, MutableSet<Note>>()
|
||||||
|
|
||||||
val reports = Collections.synchronizedSet(mutableSetOf<Note>())
|
val reports = Collections.synchronizedSet(mutableSetOf<Note>())
|
||||||
|
val relaysBeingUsed = mutableMapOf<String, RelayInfo>()
|
||||||
|
|
||||||
fun toBestDisplayName(): String {
|
fun toBestDisplayName(): String {
|
||||||
return bestDisplayName() ?: bestUsername() ?: pubkeyDisplayHex
|
return bestDisplayName() ?: bestUsername() ?: pubkeyDisplayHex
|
||||||
@ -120,6 +121,26 @@ class User(val pubkey: ByteArray) {
|
|||||||
updateSubscribers { it.onNewMessage() }
|
updateSubscribers { it.onNewMessage() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class RelayInfo (
|
||||||
|
val url: String,
|
||||||
|
var lastEvent: Long,
|
||||||
|
var counter: Long
|
||||||
|
)
|
||||||
|
|
||||||
|
fun addRelay(relay: Relay, eventTime: Long) {
|
||||||
|
val here = relaysBeingUsed.get(relay.url)
|
||||||
|
if (here == null) {
|
||||||
|
relaysBeingUsed.put(relay.url, RelayInfo(relay.url, eventTime, 1) )
|
||||||
|
} else {
|
||||||
|
if (eventTime > here.lastEvent) {
|
||||||
|
here.lastEvent = eventTime
|
||||||
|
}
|
||||||
|
here.counter++
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSubscribers { it.onNewRelayInfo() }
|
||||||
|
}
|
||||||
|
|
||||||
fun updateFollows(newFollows: Set<User>, updateAt: Long) {
|
fun updateFollows(newFollows: Set<User>, updateAt: Long) {
|
||||||
val toBeAdded = synchronized(follows) {
|
val toBeAdded = synchronized(follows) {
|
||||||
newFollows - follows
|
newFollows - follows
|
||||||
@ -191,6 +212,7 @@ class User(val pubkey: ByteArray) {
|
|||||||
open fun onFollowsChange() = Unit
|
open fun onFollowsChange() = Unit
|
||||||
open fun onNewPosts() = Unit
|
open fun onNewPosts() = Unit
|
||||||
open fun onNewMessage() = Unit
|
open fun onNewMessage() = Unit
|
||||||
|
open fun onNewRelayInfo() = Unit
|
||||||
open fun onNewReports() = Unit
|
open fun onNewReports() = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ import java.lang.Math.round
|
|||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun NewRelayListView(onClose: () -> Unit, account: Account) {
|
fun NewRelayListView(onClose: () -> Unit, account: Account, relayToAdd: String = "") {
|
||||||
val postViewModel: NewRelayListViewModel = viewModel()
|
val postViewModel: NewRelayListViewModel = viewModel()
|
||||||
|
|
||||||
val feedState by postViewModel.relays.collectAsState()
|
val feedState by postViewModel.relays.collectAsState()
|
||||||
@ -131,7 +131,7 @@ fun NewRelayListView(onClose: () -> Unit, account: Account) {
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
|
|
||||||
EditableServerConfig() {
|
EditableServerConfig(relayToAdd) {
|
||||||
postViewModel.addRelay(it)
|
postViewModel.addRelay(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -295,8 +295,8 @@ fun ServerConfig(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EditableServerConfig(onNewRelay: (NewRelayListViewModel.Relay) -> Unit) {
|
fun EditableServerConfig(relayToAdd: String, onNewRelay: (NewRelayListViewModel.Relay) -> Unit) {
|
||||||
var url by remember { mutableStateOf<String>("") }
|
var url by remember { mutableStateOf<String>(relayToAdd) }
|
||||||
var read by remember { mutableStateOf(true) }
|
var read by remember { mutableStateOf(true) }
|
||||||
var write by remember { mutableStateOf(true) }
|
var write by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.note
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.Button
|
||||||
|
import androidx.compose.material.ButtonDefaults
|
||||||
|
import androidx.compose.material.Divider
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.vitorpamplona.amethyst.model.User
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RelayCompose(
|
||||||
|
relay: User.RelayInfo,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
navController: NavController,
|
||||||
|
onAddRelay: () -> Unit,
|
||||||
|
onRemoveRelay: () -> Unit
|
||||||
|
) {
|
||||||
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
|
val account = accountState?.account ?: return
|
||||||
|
|
||||||
|
val ctx = LocalContext.current.applicationContext
|
||||||
|
|
||||||
|
Column() {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 12.dp, end = 12.dp, top = 10.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
//UserPicture(user, navController, account.userProfile(), 55.dp)
|
||||||
|
|
||||||
|
Column(modifier = Modifier
|
||||||
|
.padding(start = 10.dp)
|
||||||
|
.weight(1f)) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Text(
|
||||||
|
relay.url.trim().removePrefix("wss://"),
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
timeAgo(relay.lastEvent),
|
||||||
|
maxLines = 1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"${relay.counter} events received",
|
||||||
|
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(modifier = Modifier.padding(start = 10.dp)) {
|
||||||
|
if (account.activeRelays()?.filter { it.url == relay.url }?.isEmpty() == true) {
|
||||||
|
AddRelayButton { onAddRelay() }
|
||||||
|
} else {
|
||||||
|
RemoveRelayButton {
|
||||||
|
onRemoveRelay()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Divider(
|
||||||
|
modifier = Modifier.padding(top = 10.dp),
|
||||||
|
thickness = 0.25.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AddRelayButton(onClick: () -> Unit) {
|
||||||
|
Button(
|
||||||
|
modifier = Modifier.padding(horizontal = 3.dp),
|
||||||
|
onClick = onClick,
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = ButtonDefaults
|
||||||
|
.buttonColors(
|
||||||
|
backgroundColor = MaterialTheme.colors.primary
|
||||||
|
),
|
||||||
|
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
Text(text = "Add", color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RemoveRelayButton(onClick: () -> Unit) {
|
||||||
|
Button(
|
||||||
|
modifier = Modifier.padding(horizontal = 3.dp),
|
||||||
|
onClick = onClick,
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
colors = ButtonDefaults
|
||||||
|
.buttonColors(
|
||||||
|
backgroundColor = MaterialTheme.colors.primary
|
||||||
|
),
|
||||||
|
contentPadding = PaddingValues(vertical = 6.dp, horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
Text(text = "Remove", color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun formattedDateTime(timestamp: Long): String {
|
||||||
|
return Instant.ofEpochSecond(timestamp).atZone(ZoneId.systemDefault())
|
||||||
|
.format(DateTimeFormatter.ofPattern("MMM d, uuuu hh:mm a"))
|
||||||
|
}
|
@ -4,6 +4,7 @@ import android.text.format.DateUtils
|
|||||||
|
|
||||||
fun timeAgo(mills: Long?): String {
|
fun timeAgo(mills: Long?): String {
|
||||||
if (mills == null) return " "
|
if (mills == null) return " "
|
||||||
|
if (mills == 0L) return " • never"
|
||||||
|
|
||||||
var humanReadable = DateUtils.getRelativeTimeSpanString(
|
var humanReadable = DateUtils.getRelativeTimeSpanString(
|
||||||
mills * 1000,
|
mills * 1000,
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
package com.vitorpamplona.amethyst.ui.screen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
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.unit.dp
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||||
|
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||||
|
import com.vitorpamplona.amethyst.model.User
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.RelayCompose
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class RelayFeedViewModel: ViewModel() {
|
||||||
|
val order = compareByDescending<User.RelayInfo> { it.lastEvent }.thenByDescending { it.counter }.thenBy { it.url }
|
||||||
|
|
||||||
|
private val _feedContent = MutableStateFlow<List<User.RelayInfo>>(emptyList())
|
||||||
|
val feedContent = _feedContent.asStateFlow()
|
||||||
|
|
||||||
|
var currentUser: User? = null
|
||||||
|
|
||||||
|
fun refresh() {
|
||||||
|
val beingUsed = currentUser?.relaysBeingUsed?.values?.toList() ?: emptyList()
|
||||||
|
val beingUsedSet = currentUser?.relaysBeingUsed?.keys ?: emptySet()
|
||||||
|
|
||||||
|
val newRelaysFromRecord = currentUser?.relays?.entries?.mapNotNull {
|
||||||
|
if (it.key !in beingUsedSet) {
|
||||||
|
User.RelayInfo(it.key, 0, 0)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} ?: emptyList()
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
_feedContent.update { (beingUsed + newRelaysFromRecord).sortedWith(order) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class CacheListener: User.Listener() {
|
||||||
|
override fun onNewRelayInfo() { refresh() }
|
||||||
|
override fun onRelayChange() { refresh() }
|
||||||
|
}
|
||||||
|
|
||||||
|
val listener = CacheListener()
|
||||||
|
|
||||||
|
fun subscribeTo(user: User) {
|
||||||
|
currentUser = user
|
||||||
|
user.subscribe(listener)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unsubscribeTo(user: User) {
|
||||||
|
user.unsubscribe(listener)
|
||||||
|
currentUser = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RelayFeedView(viewModel: RelayFeedViewModel, accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
|
val account = accountState?.account ?: return
|
||||||
|
|
||||||
|
val feedState by viewModel.feedContent.collectAsState()
|
||||||
|
|
||||||
|
var isRefreshing by remember { mutableStateOf(false) }
|
||||||
|
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing)
|
||||||
|
|
||||||
|
var wantsToAddRelay by remember {
|
||||||
|
mutableStateOf( "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wantsToAddRelay.isNotEmpty())
|
||||||
|
NewRelayListView({ wantsToAddRelay = "" }, account, wantsToAddRelay)
|
||||||
|
|
||||||
|
LaunchedEffect(isRefreshing) {
|
||||||
|
if (isRefreshing) {
|
||||||
|
viewModel.refresh()
|
||||||
|
isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwipeRefresh(
|
||||||
|
state = swipeRefreshState,
|
||||||
|
onRefresh = {
|
||||||
|
isRefreshing = true
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Column() {
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
contentPadding = PaddingValues(
|
||||||
|
top = 10.dp,
|
||||||
|
bottom = 10.dp
|
||||||
|
),
|
||||||
|
state = listState
|
||||||
|
) {
|
||||||
|
itemsIndexed(feedState, key = { _, item -> item.url }) { index, item ->
|
||||||
|
RelayCompose(item,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
navController = navController,
|
||||||
|
onAddRelay = { wantsToAddRelay = item.url },
|
||||||
|
onRemoveRelay = { wantsToAddRelay = item.url }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -78,6 +78,7 @@ import com.vitorpamplona.amethyst.service.NostrUserProfileFollowersDataSource
|
|||||||
import com.vitorpamplona.amethyst.service.NostrUserProfileFollowsDataSource
|
import com.vitorpamplona.amethyst.service.NostrUserProfileFollowsDataSource
|
||||||
import com.vitorpamplona.amethyst.service.model.ReportEvent
|
import com.vitorpamplona.amethyst.service.model.ReportEvent
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
||||||
|
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataView
|
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataView
|
||||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
@ -144,7 +145,7 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
|
|||||||
selected = pagerState.currentPage == 1,
|
selected = pagerState.currentPage == 1,
|
||||||
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(1) } },
|
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(1) } },
|
||||||
text = {
|
text = {
|
||||||
Text(text = "${user.follows?.size ?: "--"} Following")
|
Text(text = "${user.follows?.size ?: "--"} Follows")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -152,15 +153,24 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
|
|||||||
selected = pagerState.currentPage == 2,
|
selected = pagerState.currentPage == 2,
|
||||||
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(2) } },
|
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(2) } },
|
||||||
text = {
|
text = {
|
||||||
Text(text = "${user.followers?.size ?: "--"} Followers")
|
Text(text = "${user.followers?.size ?: "--"} Follower")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Tab(
|
||||||
|
selected = pagerState.currentPage == 3,
|
||||||
|
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(3) } },
|
||||||
|
text = {
|
||||||
|
Text(text = "${user.relaysBeingUsed.size ?: "--"} / ${user.relays?.size ?: "--"} Relays")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
HorizontalPager(count = 3, state = pagerState) {
|
HorizontalPager(count = 4, state = pagerState) {
|
||||||
when (pagerState.currentPage) {
|
when (pagerState.currentPage) {
|
||||||
0 -> TabNotes(user, accountViewModel, navController)
|
0 -> TabNotes(user, accountViewModel, navController)
|
||||||
1 -> TabFollows(user, accountViewModel, navController)
|
1 -> TabFollows(user, accountViewModel, navController)
|
||||||
2 -> TabFollowers(user, accountViewModel, navController)
|
2 -> TabFollowers(user, accountViewModel, navController)
|
||||||
|
3 -> TabRelays(user, accountViewModel, navController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,6 +354,26 @@ fun TabFollowers(user: User, accountViewModel: AccountViewModel, navController:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TabRelays(user: User, accountViewModel: AccountViewModel, navController: NavController) {
|
||||||
|
val feedViewModel: RelayFeedViewModel = viewModel()
|
||||||
|
|
||||||
|
DisposableEffect(key1 = user) {
|
||||||
|
feedViewModel.subscribeTo(user)
|
||||||
|
onDispose {
|
||||||
|
feedViewModel.unsubscribeTo(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(Modifier.fillMaxHeight()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(vertical = 0.dp)
|
||||||
|
) {
|
||||||
|
RelayFeedView(feedViewModel, accountViewModel, navController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NSecCopyButton(
|
private fun NSecCopyButton(
|
||||||
account: Account
|
account: Account
|
||||||
|
Loading…
x
Reference in New Issue
Block a user