Slightly faster reactions and zap icons
This commit is contained in:
@@ -328,10 +328,14 @@ open class Note(val idHex: String) {
|
|||||||
}.toSet()
|
}.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isReactedBy(user: User): String? {
|
fun getReactionBy(user: User): String? {
|
||||||
return reactions.filter {
|
return reactions.firstNotNullOfOrNull {
|
||||||
it.value.any { it.author?.pubkeyHex == user.pubkeyHex }
|
if (it.value.any { it.author?.pubkeyHex == user.pubkeyHex }) {
|
||||||
}.keys.firstOrNull()
|
it.key
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isBoostedBy(user: User): Boolean {
|
fun isBoostedBy(user: User): Boolean {
|
||||||
|
|||||||
@@ -308,7 +308,9 @@ fun RenderZapRaiser(baseNote: Note, zapraiserAmount: Long, details: Boolean, acc
|
|||||||
}
|
}
|
||||||
|
|
||||||
LinearProgressIndicator(
|
LinearProgressIndicator(
|
||||||
modifier = Modifier.fillMaxWidth().height(if (details) 24.dp else 4.dp),
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(if (details) 24.dp else 4.dp),
|
||||||
color = color,
|
color = color,
|
||||||
progress = zapraiserProgress
|
progress = zapraiserProgress
|
||||||
)
|
)
|
||||||
@@ -558,12 +560,17 @@ fun ReplyCounter(baseNote: Note, textColor: Color) {
|
|||||||
it.note.replies.size
|
it.note.replies.size
|
||||||
}.observeAsState(baseNote.replies.size)
|
}.observeAsState(baseNote.replies.size)
|
||||||
|
|
||||||
SlidingAnimation(repliesState, textColor)
|
SlidingAnimationCount(repliesState, textColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SlidingAnimationCount(baseCount: MutableState<Int>, textColor: Color) {
|
||||||
|
SlidingAnimationCount(baseCount.value, textColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun SlidingAnimation(baseCount: Int, textColor: Color) {
|
private fun SlidingAnimationCount(baseCount: Int, textColor: Color) {
|
||||||
AnimatedContent<Int>(
|
AnimatedContent<Int>(
|
||||||
targetState = baseCount,
|
targetState = baseCount,
|
||||||
transitionSpec = AnimatedContentScope<Int>::transitionSpec
|
transitionSpec = AnimatedContentScope<Int>::transitionSpec
|
||||||
@@ -597,9 +604,9 @@ private fun TextCount(count: Int, textColor: Color) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
private fun SlidingAnimation(amount: String, textColor: Color) {
|
private fun SlidingAnimationAmount(amount: MutableState<String>, textColor: Color) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = amount,
|
targetState = amount.value,
|
||||||
transitionSpec = AnimatedContentScope<String>::transitionSpec
|
transitionSpec = AnimatedContentScope<String>::transitionSpec
|
||||||
) { count ->
|
) { count ->
|
||||||
Text(
|
Text(
|
||||||
@@ -694,7 +701,7 @@ fun BoostText(baseNote: Note, grayTint: Color) {
|
|||||||
it.note.boosts.size
|
it.note.boosts.size
|
||||||
}.distinctUntilChanged().observeAsState(baseNote.boosts.size)
|
}.distinctUntilChanged().observeAsState(baseNote.boosts.size)
|
||||||
|
|
||||||
SlidingAnimation(boostState, grayTint)
|
SlidingAnimationCount(boostState, grayTint)
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@@ -771,32 +778,41 @@ fun LikeIcon(
|
|||||||
grayTint: Color,
|
grayTint: Color,
|
||||||
accountViewModel: AccountViewModel
|
accountViewModel: AccountViewModel
|
||||||
) {
|
) {
|
||||||
val reactionsState by baseNote.live().reactions.observeAsState()
|
val reactionType = remember(baseNote) {
|
||||||
|
|
||||||
var reactionType by remember(baseNote) {
|
|
||||||
mutableStateOf<String?>(null)
|
mutableStateOf<String?>(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(key1 = reactionsState) {
|
val scope = rememberCoroutineScope()
|
||||||
launch(Dispatchers.Default) {
|
|
||||||
val newReactionType = reactionsState?.note?.isReactedBy(accountViewModel.userProfile())?.firstFullChar()
|
WatchReactionTypeForNote(baseNote, accountViewModel) { newReactionType ->
|
||||||
if (reactionType != newReactionType) {
|
if (reactionType.value != newReactionType) {
|
||||||
launch(Dispatchers.Main) {
|
scope.launch(Dispatchers.Main) {
|
||||||
reactionType = newReactionType
|
reactionType.value = newReactionType
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Crossfade(targetState = reactionType) {
|
Crossfade(targetState = reactionType) {
|
||||||
if (it != null) {
|
val value = it.value
|
||||||
RenderReactionType(it, iconSize, iconFontSize)
|
if (value != null) {
|
||||||
|
RenderReactionType(value, iconSize, iconFontSize)
|
||||||
} else {
|
} else {
|
||||||
RenderLikeIcon(iconSize, grayTint)
|
RenderLikeIcon(iconSize, grayTint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WatchReactionTypeForNote(baseNote: Note, accountViewModel: AccountViewModel, onNewReactionType: (String?) -> Unit) {
|
||||||
|
val reactionsState by baseNote.live().reactions.observeAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = reactionsState) {
|
||||||
|
launch(Dispatchers.Default) {
|
||||||
|
onNewReactionType(reactionsState?.note?.getReactionBy(accountViewModel.userProfile())?.firstFullChar())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RenderLikeIcon(
|
private fun RenderLikeIcon(
|
||||||
iconSize: Dp = Size20dp,
|
iconSize: Dp = Size20dp,
|
||||||
@@ -841,24 +857,32 @@ private fun RenderReactionType(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LikeText(baseNote: Note, grayTint: Color) {
|
fun LikeText(baseNote: Note, grayTint: Color) {
|
||||||
val reactionsState by baseNote.live().reactions.observeAsState()
|
val reactionsCount = remember(baseNote) {
|
||||||
|
|
||||||
var reactionsCount by remember(baseNote) {
|
|
||||||
mutableStateOf(baseNote.reactions.size)
|
mutableStateOf(baseNote.reactions.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(key1 = reactionsState) {
|
val scope = rememberCoroutineScope()
|
||||||
launch(Dispatchers.Default) {
|
|
||||||
val newReactionsCount = reactionsState?.note?.countReactions() ?: 0
|
WatchReactionCountForNote(baseNote) { newReactionsCount ->
|
||||||
if (reactionsCount != newReactionsCount) {
|
if (reactionsCount.value != newReactionsCount) {
|
||||||
launch(Dispatchers.Main) {
|
scope.launch(Dispatchers.Main) {
|
||||||
reactionsCount = newReactionsCount
|
reactionsCount.value = newReactionsCount
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SlidingAnimation(reactionsCount, grayTint)
|
SlidingAnimationCount(reactionsCount, grayTint)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WatchReactionCountForNote(baseNote: Note, onNewReactionCount: (Int) -> Unit) {
|
||||||
|
val reactionsState by baseNote.live().reactions.observeAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = reactionsState) {
|
||||||
|
launch(Dispatchers.Default) {
|
||||||
|
onNewReactionCount(reactionsState?.note?.countReactions() ?: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun likeClick(
|
private fun likeClick(
|
||||||
@@ -1072,27 +1096,22 @@ private fun ZapIcon(
|
|||||||
grayTint: Color,
|
grayTint: Color,
|
||||||
accountViewModel: AccountViewModel
|
accountViewModel: AccountViewModel
|
||||||
) {
|
) {
|
||||||
var wasZappedByLoggedInUser by remember { mutableStateOf(false) }
|
val wasZappedByLoggedInUser = remember { mutableStateOf(false) }
|
||||||
val zapsState by baseNote.live().zaps.observeAsState()
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = zapsState) {
|
val scope = rememberCoroutineScope()
|
||||||
launch(Dispatchers.Default) {
|
|
||||||
zapsState?.note?.let {
|
|
||||||
if (!wasZappedByLoggedInUser) {
|
|
||||||
val newWasZapped = accountViewModel.calculateIfNoteWasZappedByAccount(it)
|
|
||||||
|
|
||||||
if (wasZappedByLoggedInUser != newWasZapped) {
|
if (!wasZappedByLoggedInUser.value) {
|
||||||
launch(Dispatchers.Main) {
|
WatchZapsForNote(baseNote, accountViewModel) { newWasZapped ->
|
||||||
wasZappedByLoggedInUser = newWasZapped
|
if (wasZappedByLoggedInUser.value != newWasZapped) {
|
||||||
}
|
scope.launch(Dispatchers.Main) {
|
||||||
}
|
wasZappedByLoggedInUser.value = newWasZapped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Crossfade(targetState = wasZappedByLoggedInUser) {
|
Crossfade(targetState = wasZappedByLoggedInUser) {
|
||||||
if (it) {
|
if (it.value) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Bolt,
|
imageVector = Icons.Default.Bolt,
|
||||||
contentDescription = stringResource(R.string.zaps),
|
contentDescription = stringResource(R.string.zaps),
|
||||||
@@ -1110,30 +1129,47 @@ private fun ZapIcon(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WatchZapsForNote(baseNote: Note, accountViewModel: AccountViewModel, onWasZapped: (Boolean) -> Unit) {
|
||||||
|
val zapsState by baseNote.live().zaps.observeAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = zapsState) {
|
||||||
|
launch(Dispatchers.Default) {
|
||||||
|
onWasZapped(accountViewModel.calculateIfNoteWasZappedByAccount(baseNote))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ZapAmountText(
|
private fun ZapAmountText(
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
grayTint: Color,
|
grayTint: Color,
|
||||||
accountViewModel: AccountViewModel
|
accountViewModel: AccountViewModel
|
||||||
) {
|
) {
|
||||||
val zapsState by baseNote.live().zaps.observeAsState()
|
val zapAmountTxt = remember(baseNote) { mutableStateOf("") }
|
||||||
|
|
||||||
var zapAmountTxt by remember { mutableStateOf("") }
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
LaunchedEffect(key1 = zapsState) {
|
WatchZapAmountsForNote(baseNote, accountViewModel) { newZapAmount ->
|
||||||
launch(Dispatchers.Default) {
|
if (zapAmountTxt.value != newZapAmount) {
|
||||||
zapsState?.note?.let {
|
scope.launch(Dispatchers.Main) {
|
||||||
val newZapAmount = showAmount(accountViewModel.calculateZapAmount(it))
|
zapAmountTxt.value = newZapAmount
|
||||||
if (newZapAmount != zapAmountTxt) {
|
|
||||||
launch(Dispatchers.Main) {
|
|
||||||
zapAmountTxt = newZapAmount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SlidingAnimation(zapAmountTxt, grayTint)
|
SlidingAnimationAmount(zapAmountTxt, grayTint)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun WatchZapAmountsForNote(baseNote: Note, accountViewModel: AccountViewModel, onZapAmount: (String) -> Unit) {
|
||||||
|
val zapsState by baseNote.live().zaps.observeAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = zapsState) {
|
||||||
|
launch(Dispatchers.Default) {
|
||||||
|
onZapAmount(showAmount(accountViewModel.calculateZapAmount(baseNote)))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -584,7 +584,7 @@ fun ChannelHeader(
|
|||||||
ShowVideoStreaming(baseChannel, accountViewModel)
|
ShowVideoStreaming(baseChannel, accountViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
var expanded = remember { mutableStateOf(false) }
|
val expanded = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.clickable {
|
modifier = modifier.clickable {
|
||||||
|
|||||||
Reference in New Issue
Block a user