- Moving postValue to IO thread
- launching navigate in a thread
This commit is contained in:
@@ -38,19 +38,15 @@ object NotificationCache {
|
|||||||
|
|
||||||
class NotificationLiveData(val cache: NotificationCache) : LiveData<NotificationState>(NotificationState(cache)) {
|
class NotificationLiveData(val cache: NotificationCache) : LiveData<NotificationState>(NotificationState(cache)) {
|
||||||
// Refreshes observers in batches.
|
// Refreshes observers in batches.
|
||||||
private val bundler = BundledUpdate(300, Dispatchers.Main) {
|
private val bundler = BundledUpdate(300, Dispatchers.IO) {
|
||||||
if (hasActiveObservers()) {
|
if (hasActiveObservers()) {
|
||||||
refresh()
|
postValue(NotificationState(cache))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invalidateData() {
|
fun invalidateData() {
|
||||||
bundler.invalidate()
|
bundler.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refresh() {
|
|
||||||
postValue(NotificationState(cache))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotificationState(val cache: NotificationCache)
|
class NotificationState(val cache: NotificationCache)
|
||||||
|
|||||||
@@ -55,19 +55,15 @@ class AntiSpamFilter {
|
|||||||
class AntiSpamLiveData(val cache: AntiSpamFilter) : LiveData<AntiSpamState>(AntiSpamState(cache)) {
|
class AntiSpamLiveData(val cache: AntiSpamFilter) : LiveData<AntiSpamState>(AntiSpamState(cache)) {
|
||||||
|
|
||||||
// Refreshes observers in batches.
|
// Refreshes observers in batches.
|
||||||
private val bundler = BundledUpdate(300, Dispatchers.Main) {
|
private val bundler = BundledUpdate(300, Dispatchers.IO) {
|
||||||
if (hasActiveObservers()) {
|
if (hasActiveObservers()) {
|
||||||
refresh()
|
postValue(AntiSpamState(cache))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invalidateData() {
|
fun invalidateData() {
|
||||||
bundler.invalidate()
|
bundler.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refresh() {
|
|
||||||
postValue(AntiSpamState(cache))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AntiSpamState(val cache: AntiSpamFilter)
|
class AntiSpamState(val cache: AntiSpamFilter)
|
||||||
|
|||||||
@@ -74,9 +74,9 @@ class Channel(val idHex: String) {
|
|||||||
|
|
||||||
class ChannelLiveData(val channel: Channel) : LiveData<ChannelState>(ChannelState(channel)) {
|
class ChannelLiveData(val channel: Channel) : LiveData<ChannelState>(ChannelState(channel)) {
|
||||||
// Refreshes observers in batches.
|
// Refreshes observers in batches.
|
||||||
private val bundler = BundledUpdate(300, Dispatchers.Main) {
|
private val bundler = BundledUpdate(300, Dispatchers.IO) {
|
||||||
if (hasActiveObservers()) {
|
if (hasActiveObservers()) {
|
||||||
refresh()
|
postValue(ChannelState(channel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,10 +84,6 @@ class ChannelLiveData(val channel: Channel) : LiveData<ChannelState>(ChannelStat
|
|||||||
bundler.invalidate()
|
bundler.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refresh() {
|
|
||||||
postValue(ChannelState(channel))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActive() {
|
override fun onActive() {
|
||||||
super.onActive()
|
super.onActive()
|
||||||
NostrSingleChannelDataSource.add(channel.idHex)
|
NostrSingleChannelDataSource.add(channel.idHex)
|
||||||
|
|||||||
@@ -916,7 +916,7 @@ object LocalCache {
|
|||||||
class LocalCacheLiveData : LiveData<Set<Note>>(setOf<Note>()) {
|
class LocalCacheLiveData : LiveData<Set<Note>>(setOf<Note>()) {
|
||||||
|
|
||||||
// Refreshes observers in batches.
|
// Refreshes observers in batches.
|
||||||
private val bundler = BundledInsert<Note>(300, Dispatchers.Main)
|
private val bundler = BundledInsert<Note>(300, Dispatchers.IO)
|
||||||
|
|
||||||
fun invalidateData(newNote: Note) {
|
fun invalidateData(newNote: Note) {
|
||||||
bundler.invalidateList(newNote) { bundledNewNotes ->
|
bundler.invalidateList(newNote) { bundledNewNotes ->
|
||||||
|
|||||||
@@ -410,9 +410,9 @@ class NoteLiveSet(u: Note) {
|
|||||||
|
|
||||||
class NoteLiveData(val note: Note) : LiveData<NoteState>(NoteState(note)) {
|
class NoteLiveData(val note: Note) : LiveData<NoteState>(NoteState(note)) {
|
||||||
// Refreshes observers in batches.
|
// Refreshes observers in batches.
|
||||||
private val bundler = BundledUpdate(300, Dispatchers.Main) {
|
private val bundler = BundledUpdate(300, Dispatchers.IO) {
|
||||||
if (hasActiveObservers()) {
|
if (hasActiveObservers()) {
|
||||||
refresh()
|
postValue(NoteState(note))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -420,10 +420,6 @@ class NoteLiveData(val note: Note) : LiveData<NoteState>(NoteState(note)) {
|
|||||||
bundler.invalidate()
|
bundler.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refresh() {
|
|
||||||
postValue(NoteState(note))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActive() {
|
override fun onActive() {
|
||||||
super.onActive()
|
super.onActive()
|
||||||
if (note is AddressableNote) {
|
if (note is AddressableNote) {
|
||||||
|
|||||||
@@ -396,9 +396,9 @@ class UserMetadata {
|
|||||||
|
|
||||||
class UserLiveData(val user: User) : LiveData<UserState>(UserState(user)) {
|
class UserLiveData(val user: User) : LiveData<UserState>(UserState(user)) {
|
||||||
// Refreshes observers in batches.
|
// Refreshes observers in batches.
|
||||||
private val bundler = BundledUpdate(300, Dispatchers.Main) {
|
private val bundler = BundledUpdate(300, Dispatchers.IO) {
|
||||||
if (hasActiveObservers()) {
|
if (hasActiveObservers()) {
|
||||||
refresh()
|
postValue(UserState(user))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,10 +406,6 @@ class UserLiveData(val user: User) : LiveData<UserState>(UserState(user)) {
|
|||||||
bundler.invalidate()
|
bundler.invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refresh() {
|
|
||||||
postValue(UserState(user))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActive() {
|
override fun onActive() {
|
||||||
super.onActive()
|
super.onActive()
|
||||||
NostrSingleUserDataSource.add(user)
|
NostrSingleUserDataSource.add(user)
|
||||||
|
|||||||
@@ -75,10 +75,12 @@ fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForL
|
|||||||
.background(backgroundColor)
|
.background(backgroundColor)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
routeFor(
|
scope.launch {
|
||||||
note,
|
routeFor(
|
||||||
accountViewModel.userProfile()
|
note,
|
||||||
)?.let { navController.navigate(it) }
|
accountViewModel.userProfile()
|
||||||
|
)?.let { navController.navigate(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onLongClick = { popupExpanded = true }
|
onLongClick = { popupExpanded = true }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -31,6 +32,7 @@ import com.vitorpamplona.amethyst.R
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.BoostSetCard
|
import com.vitorpamplona.amethyst.ui.screen.BoostSetCard
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@@ -45,6 +47,8 @@ fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, ro
|
|||||||
val noteEvent = note?.event
|
val noteEvent = note?.event
|
||||||
var popupExpanded by remember { mutableStateOf(false) }
|
var popupExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
BlankNote(Modifier, isInnerNote)
|
BlankNote(Modifier, isInnerNote)
|
||||||
} else {
|
} else {
|
||||||
@@ -65,12 +69,19 @@ fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, ro
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.background(backgroundColor).combinedClickable(
|
modifier = Modifier
|
||||||
onClick = {
|
.background(backgroundColor)
|
||||||
routeFor(note, account.userProfile())?.let { navController.navigate(it) }
|
.combinedClickable(
|
||||||
},
|
onClick = {
|
||||||
onLongClick = { popupExpanded = true }
|
scope.launch {
|
||||||
)
|
routeFor(
|
||||||
|
note,
|
||||||
|
account.userProfile()
|
||||||
|
)?.let { navController.navigate(it) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongClick = { popupExpanded = true }
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -90,7 +101,9 @@ fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, ro
|
|||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.ic_retweeted),
|
painter = painterResource(R.drawable.ic_retweeted),
|
||||||
null,
|
null,
|
||||||
modifier = Modifier.size(16.dp).align(Alignment.TopEnd),
|
modifier = Modifier
|
||||||
|
.size(16.dp)
|
||||||
|
.align(Alignment.TopEnd),
|
||||||
tint = Color.Unspecified
|
tint = Color.Unspecified
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -31,6 +32,7 @@ import com.vitorpamplona.amethyst.R
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.LikeSetCard
|
import com.vitorpamplona.amethyst.ui.screen.LikeSetCard
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@@ -44,6 +46,7 @@ fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, route
|
|||||||
|
|
||||||
val noteEvent = note?.event
|
val noteEvent = note?.event
|
||||||
var popupExpanded by remember { mutableStateOf(false) }
|
var popupExpanded by remember { mutableStateOf(false) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
BlankNote(Modifier, isInnerNote)
|
BlankNote(Modifier, isInnerNote)
|
||||||
@@ -65,12 +68,19 @@ fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, route
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.background(backgroundColor).combinedClickable(
|
modifier = Modifier
|
||||||
onClick = {
|
.background(backgroundColor)
|
||||||
routeFor(note, account.userProfile())?.let { navController.navigate(it) }
|
.combinedClickable(
|
||||||
},
|
onClick = {
|
||||||
onLongClick = { popupExpanded = true }
|
scope.launch {
|
||||||
)
|
routeFor(
|
||||||
|
note,
|
||||||
|
account.userProfile()
|
||||||
|
)?.let { navController.navigate(it) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongClick = { popupExpanded = true }
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -90,7 +100,9 @@ fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, route
|
|||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.ic_liked),
|
painter = painterResource(R.drawable.ic_liked),
|
||||||
null,
|
null,
|
||||||
modifier = Modifier.size(16.dp).align(Alignment.TopEnd),
|
modifier = Modifier
|
||||||
|
.size(16.dp)
|
||||||
|
.align(Alignment.TopEnd),
|
||||||
tint = Color.Unspecified
|
tint = Color.Unspecified
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,12 @@ fun MessageSetCompose(messageSetCard: MessageSetCard, isInnerNote: Boolean = fal
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.background(backgroundColor).combinedClickable(
|
modifier = Modifier.background(backgroundColor).combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
routeFor(note, accountViewModel.userProfile())?.let { navController.navigate(it) }
|
scope.launch {
|
||||||
|
routeFor(
|
||||||
|
note,
|
||||||
|
accountViewModel.userProfile()
|
||||||
|
)?.let { navController.navigate(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onLongClick = { popupExpanded = true }
|
onLongClick = { popupExpanded = true }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
|
|||||||
.background(backgroundColor)
|
.background(backgroundColor)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
routeFor(note, account.userProfile())?.let { navController.navigate(it) }
|
scope.launch {
|
||||||
|
routeFor(note, account.userProfile())?.let { navController.navigate(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onLongClick = { popupExpanded = true }
|
onLongClick = { popupExpanded = true }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -274,7 +274,9 @@ fun NoteComposeInner(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = {
|
onClick = {
|
||||||
routeFor(note, loggedIn)?.let { navController.navigate(it) }
|
scope.launch {
|
||||||
|
routeFor(note, loggedIn)?.let { navController.navigate(it) }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onLongClick = { popupExpanded = true }
|
onLongClick = { popupExpanded = true }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -33,6 +34,7 @@ import com.vitorpamplona.amethyst.ui.screen.ZapSetCard
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@@ -46,6 +48,7 @@ fun ZapSetCompose(zapSetCard: ZapSetCard, isInnerNote: Boolean = false, routeFor
|
|||||||
|
|
||||||
val noteEvent = note?.event
|
val noteEvent = note?.event
|
||||||
var popupExpanded by remember { mutableStateOf(false) }
|
var popupExpanded by remember { mutableStateOf(false) }
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
if (note == null) {
|
if (note == null) {
|
||||||
BlankNote(Modifier, isInnerNote)
|
BlankNote(Modifier, isInnerNote)
|
||||||
@@ -67,12 +70,19 @@ fun ZapSetCompose(zapSetCard: ZapSetCard, isInnerNote: Boolean = false, routeFor
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.background(backgroundColor).combinedClickable(
|
modifier = Modifier
|
||||||
onClick = {
|
.background(backgroundColor)
|
||||||
routeFor(note, account.userProfile())?.let { navController.navigate(it) }
|
.combinedClickable(
|
||||||
},
|
onClick = {
|
||||||
onLongClick = { popupExpanded = true }
|
scope.launch {
|
||||||
)
|
routeFor(
|
||||||
|
note,
|
||||||
|
account.userProfile()
|
||||||
|
)?.let { navController.navigate(it) }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLongClick = { popupExpanded = true }
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ fun ChatroomFeedView(viewModel: FeedViewModel, accountViewModel: AccountViewMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FeedState.Loading -> {
|
is FeedState.Loading -> {
|
||||||
LoadingFeed()
|
LoadingFeed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import kotlinx.coroutines.launch
|
|||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel, startingPage: String? = null) {
|
fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel, startingPage: String? = null) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
|
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
|
||||||
val sheetState = rememberModalBottomSheetState(
|
val sheetState = rememberModalBottomSheetState(
|
||||||
@@ -69,7 +69,7 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
|
|||||||
drawerContent = {
|
drawerContent = {
|
||||||
DrawerContent(navController, scaffoldState, sheetState, accountViewModel)
|
DrawerContent(navController, scaffoldState, sheetState, accountViewModel)
|
||||||
BackHandler(enabled = scaffoldState.drawerState.isOpen) {
|
BackHandler(enabled = scaffoldState.drawerState.isOpen) {
|
||||||
coroutineScope.launch { scaffoldState.drawerState.close() }
|
scope.launch { scaffoldState.drawerState.close() }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
|
|||||||
Reference in New Issue
Block a user