mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-27 19:16:40 +02:00
Activates reactions and zaps to the channel and community headers.
This commit is contained in:
@@ -27,6 +27,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
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.Companion.BottomStart
|
import androidx.compose.ui.Alignment.Companion.BottomStart
|
||||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||||
import androidx.compose.ui.Alignment.Companion.TopEnd
|
import androidx.compose.ui.Alignment.Companion.TopEnd
|
||||||
@@ -347,7 +348,6 @@ private fun RenderNoteRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RenderLiveActivityThumb(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
fun RenderLiveActivityThumb(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
val noteEvent = baseNote.event as? LiveActivitiesEvent ?: return
|
val noteEvent = baseNote.event as? LiveActivitiesEvent ?: return
|
||||||
@@ -570,7 +570,7 @@ fun RenderCommunitiesThumb(baseNote: Note, accountViewModel: AccountViewModel, n
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalArrangement = Arrangement.SpaceBetween
|
verticalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
Row() {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = name,
|
text = name,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
@@ -684,7 +684,7 @@ fun RenderChannelThumb(baseNote: Note, channel: Channel, accountViewModel: Accou
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth().fillMaxHeight()
|
modifier = Modifier.fillMaxWidth().fillMaxHeight()
|
||||||
) {
|
) {
|
||||||
Row() {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = name,
|
text = name,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
|
@@ -478,20 +478,144 @@ fun CommunityHeader(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
var expanded = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
modifier = modifier.clickable {
|
||||||
.fillMaxWidth()
|
expanded.value = !expanded.value
|
||||||
.clickable {
|
|
||||||
scope.launch {
|
|
||||||
nav("Community/${baseNote.idHex}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val channelState by baseNote.live().metadata.observeAsState()
|
ShortCommunityHeader(baseNote, expanded, accountViewModel, nav)
|
||||||
val noteEvent = remember(channelState) { channelState?.note?.event as? CommunityDefinitionEvent } ?: return
|
|
||||||
|
if (expanded.value) {
|
||||||
|
LongCommunityHeader(baseNote, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showBottomDiviser) {
|
||||||
|
Divider(
|
||||||
|
thickness = 0.25.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LongCommunityHeader(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
|
val noteState by baseNote.live().metadata.observeAsState()
|
||||||
|
val noteEvent = remember(noteState) { noteState?.note?.event as? CommunityDefinitionEvent } ?: return
|
||||||
|
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 10.dp)
|
||||||
|
) {
|
||||||
|
val summary = remember(noteState) {
|
||||||
|
noteEvent.description()?.ifBlank { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(start = 10.dp)
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(
|
||||||
|
text = summary ?: "This group does not have a description or rules. Talk to the owner to add one"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column() {
|
||||||
|
Row() {
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
LongCommunityActionOptions(baseNote, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val rules = remember(noteState) {
|
||||||
|
noteEvent.rules()?.ifBlank { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
rules?.let {
|
||||||
|
Spacer(DoubleVertSpacer)
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 10.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(DoubleVertSpacer)
|
||||||
|
|
||||||
|
Row(Modifier.fillMaxWidth().padding(start = 10.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.owner),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.width(75.dp)
|
||||||
|
)
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
NoteAuthorPicture(baseNote, nav, accountViewModel, Size25dp)
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
NoteUsernameDisplay(baseNote, remember { Modifier.weight(1f) })
|
||||||
|
TimeAgo(baseNote)
|
||||||
|
MoreOptionsButton(baseNote, accountViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
var participantUsers by remember(baseNote) {
|
||||||
|
mutableStateOf<ImmutableList<Pair<Participant, User>>>(
|
||||||
|
persistentListOf()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = noteState) {
|
||||||
|
launch(Dispatchers.IO) {
|
||||||
|
val noteEvent = (noteState?.note?.event as? CommunityDefinitionEvent)
|
||||||
|
val newParticipantUsers = noteEvent?.moderators()?.mapNotNull { part ->
|
||||||
|
LocalCache.checkGetOrCreateUser(part.key)?.let { Pair(part, it) }
|
||||||
|
}?.toImmutableList()
|
||||||
|
|
||||||
|
if (newParticipantUsers != null && !equalImmutableLists(newParticipantUsers, participantUsers)) {
|
||||||
|
participantUsers = newParticipantUsers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
participantUsers.forEach {
|
||||||
|
Row(
|
||||||
|
Modifier.fillMaxWidth()
|
||||||
|
.padding(start = 10.dp, top = 10.dp)
|
||||||
|
.clickable {
|
||||||
|
nav("User/${it.second.pubkeyHex}")
|
||||||
|
},
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
it.first.role?.let { it1 ->
|
||||||
|
Text(
|
||||||
|
text = it1.capitalize(Locale.ROOT),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.width(75.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
ClickableUserPicture(it.second, Size25dp, accountViewModel)
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
UsernameDisplay(it.second, remember { Modifier.weight(1f) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ShortCommunityHeader(baseNote: Note, expanded: MutableState<Boolean>, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||||
|
val noteState by baseNote.live().metadata.observeAsState()
|
||||||
|
val noteEvent = remember(noteState) { noteState?.note?.event as? CommunityDefinitionEvent } ?: return
|
||||||
|
|
||||||
Column(modifier = modifier) {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
noteEvent.image()?.let {
|
noteEvent.image()?.let {
|
||||||
RobohashAsyncImageProxy(
|
RobohashAsyncImageProxy(
|
||||||
@@ -510,23 +634,24 @@ fun CommunityHeader(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 10.dp)
|
.padding(start = 10.dp)
|
||||||
|
.height(Size35dp)
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = remember(channelState) { noteEvent.dTag() },
|
text = remember(noteState) { noteEvent.dTag() },
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val summary = remember(channelState) {
|
val summary = remember(noteState) {
|
||||||
noteEvent.description()?.ifBlank { null }
|
noteEvent.description()?.ifBlank { null }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (summary != null) {
|
if (summary != null && !expanded.value) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = summary,
|
text = summary,
|
||||||
@@ -545,21 +670,37 @@ fun CommunityHeader(
|
|||||||
.padding(start = 5.dp),
|
.padding(start = 5.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
CommunityActionOptions(baseNote, accountViewModel, nav)
|
ShortCommunityActionOptions(baseNote, accountViewModel, nav)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showBottomDiviser) {
|
|
||||||
Divider(
|
|
||||||
thickness = 0.25.dp
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CommunityActionOptions(
|
private fun ShortCommunityActionOptions(
|
||||||
|
note: Note,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
|
val isFollowing by remember(accountState) {
|
||||||
|
derivedStateOf {
|
||||||
|
accountState?.account?.followingCommunities?.contains(note.idHex) ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
LikeReaction(baseNote = note, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel)
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
ZapReaction(baseNote = note, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel)
|
||||||
|
|
||||||
|
if (!isFollowing) {
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
JoinCommunityButton(accountViewModel, note, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LongCommunityActionOptions(
|
||||||
note: Note,
|
note: Note,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
@@ -573,8 +714,6 @@ private fun CommunityActionOptions(
|
|||||||
|
|
||||||
if (isFollowing) {
|
if (isFollowing) {
|
||||||
LeaveCommunityButton(accountViewModel, note, nav)
|
LeaveCommunityButton(accountViewModel, note, nav)
|
||||||
} else {
|
|
||||||
JoinCommunityButton(accountViewModel, note, nav)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,6 +44,7 @@ import androidx.compose.material.icons.filled.Share
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
@@ -75,6 +76,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
|
import androidx.lifecycle.distinctUntilChanged
|
||||||
|
import androidx.lifecycle.map
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.Channel
|
import com.vitorpamplona.amethyst.model.Channel
|
||||||
@@ -82,35 +85,50 @@ import com.vitorpamplona.amethyst.model.LiveActivitiesChannel
|
|||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
||||||
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
||||||
import com.vitorpamplona.amethyst.service.model.LiveActivitiesEvent.Companion.STATUS_LIVE
|
import com.vitorpamplona.amethyst.service.model.LiveActivitiesEvent.Companion.STATUS_LIVE
|
||||||
|
import com.vitorpamplona.amethyst.service.model.Participant
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
import com.vitorpamplona.amethyst.ui.actions.NewMessageTagger
|
||||||
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
|
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.actions.PostButton
|
import com.vitorpamplona.amethyst.ui.actions.PostButton
|
||||||
import com.vitorpamplona.amethyst.ui.actions.ServersAvailable
|
import com.vitorpamplona.amethyst.ui.actions.ServersAvailable
|
||||||
import com.vitorpamplona.amethyst.ui.actions.UploadFromGallery
|
import com.vitorpamplona.amethyst.ui.actions.UploadFromGallery
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImageProxy
|
||||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||||
import com.vitorpamplona.amethyst.ui.components.ZoomableUrlVideo
|
import com.vitorpamplona.amethyst.ui.components.ZoomableUrlVideo
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
|
||||||
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
|
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||||
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.MoreOptionsButton
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.NoteUsernameDisplay
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.TimeAgo
|
||||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||||
import com.vitorpamplona.amethyst.ui.note.ZapReaction
|
import com.vitorpamplona.amethyst.ui.note.ZapReaction
|
||||||
import com.vitorpamplona.amethyst.ui.note.timeAgo
|
import com.vitorpamplona.amethyst.ui.note.timeAgo
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.RefreshingChatroomFeedView
|
import com.vitorpamplona.amethyst.ui.screen.RefreshingChatroomFeedView
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.equalImmutableLists
|
||||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size25dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.SmallBorder
|
import com.vitorpamplona.amethyst.ui.theme.SmallBorder
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChannelScreen(
|
fun ChannelScreen(
|
||||||
@@ -556,47 +574,73 @@ fun ChannelHeader(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
Column(Modifier.fillMaxWidth()) {
|
||||||
Column(
|
if (showVideo && baseChannel is LiveActivitiesChannel) {
|
||||||
Modifier
|
ShowVideoStreaming(baseChannel)
|
||||||
.fillMaxWidth()
|
|
||||||
.clickable {
|
|
||||||
scope.launch {
|
|
||||||
nav("Channel/${baseChannel.idHex}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var expanded = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier.clickable {
|
||||||
|
expanded.value = !expanded.value
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
val channelState by baseChannel.live.observeAsState()
|
ShortChannelHeader(baseChannel, expanded, accountViewModel, nav, showFlag)
|
||||||
val channel = remember(channelState) { channelState?.channel } ?: return
|
|
||||||
|
|
||||||
if (showVideo) {
|
if (expanded.value) {
|
||||||
val streamingUrl by remember(channelState) {
|
LongChannelHeader(baseChannel, accountViewModel, nav)
|
||||||
derivedStateOf {
|
}
|
||||||
val activity = channel as? LiveActivitiesChannel
|
}
|
||||||
val description = activity?.info?.title()
|
|
||||||
val url = activity?.info?.streaming()
|
if (showBottomDiviser) {
|
||||||
if (url != null) {
|
Divider(
|
||||||
ZoomableUrlVideo(url, description = description)
|
thickness = 0.25.dp
|
||||||
} else {
|
)
|
||||||
null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ShowVideoStreaming(
|
||||||
|
baseChannel: LiveActivitiesChannel
|
||||||
|
) {
|
||||||
|
val streamingUrl by baseChannel.live.map {
|
||||||
|
val activity = it.channel as? LiveActivitiesChannel
|
||||||
|
activity?.info?.streaming()
|
||||||
|
}.distinctUntilChanged().observeAsState(baseChannel.info?.streaming())
|
||||||
|
|
||||||
streamingUrl?.let {
|
streamingUrl?.let {
|
||||||
CheckIfUrlIsOnline(it.url) {
|
CheckIfUrlIsOnline(it) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = remember { Modifier.heightIn(max = 300.dp) }
|
modifier = remember { Modifier.heightIn(max = 300.dp) }
|
||||||
) {
|
) {
|
||||||
|
val zoomableUrlVideo = remember(it) {
|
||||||
|
ZoomableUrlVideo(url = it)
|
||||||
|
}
|
||||||
|
|
||||||
ZoomableContentView(
|
ZoomableContentView(
|
||||||
content = it
|
content = zoomableUrlVideo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(modifier = modifier) {
|
|
||||||
|
@Composable
|
||||||
|
private fun ShortChannelHeader(
|
||||||
|
baseChannel: Channel,
|
||||||
|
expanded: MutableState<Boolean>,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit,
|
||||||
|
showFlag: Boolean
|
||||||
|
) {
|
||||||
|
val channelState = baseChannel.live.observeAsState()
|
||||||
|
val channel = remember(channelState) {
|
||||||
|
channelState.value?.channel
|
||||||
|
} ?: return
|
||||||
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
if (channel is LiveActivitiesChannel) {
|
if (channel is LiveActivitiesChannel) {
|
||||||
channel.creator?.let {
|
channel.creator?.let {
|
||||||
@@ -614,7 +658,8 @@ fun ChannelHeader(
|
|||||||
model = it,
|
model = it,
|
||||||
contentDescription = stringResource(R.string.profile_image),
|
contentDescription = stringResource(R.string.profile_image),
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier.padding(start = 10.dp)
|
modifier = Modifier
|
||||||
|
.padding(start = 10.dp)
|
||||||
.width(Size35dp)
|
.width(Size35dp)
|
||||||
.height(Size35dp)
|
.height(Size35dp)
|
||||||
.clip(shape = CircleShape)
|
.clip(shape = CircleShape)
|
||||||
@@ -625,6 +670,7 @@ fun ChannelHeader(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(start = 10.dp)
|
.padding(start = 10.dp)
|
||||||
|
.height(35.dp)
|
||||||
.weight(1f),
|
.weight(1f),
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
@@ -641,7 +687,7 @@ fun ChannelHeader(
|
|||||||
channel.summary()?.ifBlank { null }
|
channel.summary()?.ifBlank { null }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (summary != null) {
|
if (summary != null && !expanded.value) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Text(
|
Text(
|
||||||
text = summary,
|
text = summary,
|
||||||
@@ -661,7 +707,7 @@ fun ChannelHeader(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
if (channel is PublicChatChannel) {
|
if (channel is PublicChatChannel) {
|
||||||
ChannelActionOptions(channel, accountViewModel, nav)
|
ShortChannelActionOptions(channel, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
if (channel is LiveActivitiesChannel) {
|
if (channel is LiveActivitiesChannel) {
|
||||||
LiveChannelActionOptions(channel, showFlag, accountViewModel, nav)
|
LiveChannelActionOptions(channel, showFlag, accountViewModel, nav)
|
||||||
@@ -670,22 +716,150 @@ fun ChannelHeader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showBottomDiviser) {
|
@Composable
|
||||||
Divider(
|
private fun LongChannelHeader(
|
||||||
thickness = 0.25.dp
|
baseChannel: Channel,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val channelState = baseChannel.live.observeAsState()
|
||||||
|
val channel = remember(channelState) {
|
||||||
|
channelState.value?.channel
|
||||||
|
} ?: return
|
||||||
|
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 10.dp)
|
||||||
|
) {
|
||||||
|
val summary = remember(channelState) {
|
||||||
|
channel.summary()?.ifBlank { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(start = 10.dp)
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
Text(
|
||||||
|
text = summary ?: "This group does not have a description or rules. Talk to the owner to add one"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Column() {
|
||||||
|
if (channel is PublicChatChannel) {
|
||||||
|
Row() {
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
LongChannelActionOptions(channel, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(DoubleVertSpacer)
|
||||||
|
|
||||||
|
Row(Modifier.fillMaxWidth().padding(start = 10.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
LoadNote(baseNoteHex = channel.idHex) {
|
||||||
|
it?.let {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.owner),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.width(55.dp)
|
||||||
|
)
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
NoteAuthorPicture(it, nav, accountViewModel, Size25dp)
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
NoteUsernameDisplay(it, remember { Modifier.weight(1f) })
|
||||||
|
TimeAgo(it)
|
||||||
|
MoreOptionsButton(it, accountViewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var participantUsers by remember(baseChannel) {
|
||||||
|
mutableStateOf<ImmutableList<Pair<Participant, User>>>(
|
||||||
|
persistentListOf()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel is LiveActivitiesChannel) {
|
||||||
|
LaunchedEffect(key1 = channelState) {
|
||||||
|
launch(Dispatchers.IO) {
|
||||||
|
val newParticipantUsers = channel.info?.participants()?.mapNotNull { part ->
|
||||||
|
LocalCache.checkGetOrCreateUser(part.key)?.let { Pair(part, it) }
|
||||||
|
}?.toImmutableList()
|
||||||
|
|
||||||
|
if (newParticipantUsers != null && !equalImmutableLists(newParticipantUsers, participantUsers)) {
|
||||||
|
participantUsers = newParticipantUsers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
participantUsers.forEach {
|
||||||
|
Row(
|
||||||
|
Modifier.fillMaxWidth()
|
||||||
|
.padding(start = 10.dp, top = 10.dp)
|
||||||
|
.clickable {
|
||||||
|
nav("User/${it.second.pubkeyHex}")
|
||||||
|
},
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
it.first.role?.let { it1 ->
|
||||||
|
Text(
|
||||||
|
text = it1.capitalize(Locale.ROOT),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier.width(55.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
ClickableUserPicture(it.second, Size25dp, accountViewModel)
|
||||||
|
Spacer(DoubleHorzSpacer)
|
||||||
|
UsernameDisplay(it.second, remember { Modifier.weight(1f) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ChannelActionOptions(
|
private fun ShortChannelActionOptions(
|
||||||
channel: PublicChatChannel,
|
channel: PublicChatChannel,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit
|
nav: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
NoteCopyButton(channel)
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
|
val isFollowing by remember(accountState) {
|
||||||
|
derivedStateOf {
|
||||||
|
accountState?.account?.followingChannels?.contains(channel.idHex) ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadNote(baseNoteHex = channel.idHex) {
|
||||||
|
it?.let {
|
||||||
|
var popupExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
LikeReaction(baseNote = it, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel)
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
ZapReaction(baseNote = it, grayTint = MaterialTheme.colors.onSurface, accountViewModel = accountViewModel)
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isFollowing) {
|
||||||
|
JoinChatButton(accountViewModel, channel, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LongChannelActionOptions(
|
||||||
|
channel: PublicChatChannel,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit
|
||||||
|
) {
|
||||||
val isMe by remember(accountViewModel) {
|
val isMe by remember(accountViewModel) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
channel.creator == accountViewModel.account.userProfile()
|
channel.creator == accountViewModel.account.userProfile()
|
||||||
@@ -705,8 +879,6 @@ private fun ChannelActionOptions(
|
|||||||
|
|
||||||
if (isFollowing) {
|
if (isFollowing) {
|
||||||
LeaveChatButton(accountViewModel, channel, nav)
|
LeaveChatButton(accountViewModel, channel, nav)
|
||||||
} else {
|
|
||||||
JoinChatButton(accountViewModel, channel, nav)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,7 +1063,6 @@ fun LeaveChatButton(accountViewModel: AccountViewModel, channel: Channel, nav: (
|
|||||||
modifier = Modifier.padding(horizontal = 3.dp),
|
modifier = Modifier.padding(horizontal = 3.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
accountViewModel.account.leaveChannel(channel.idHex)
|
accountViewModel.account.leaveChannel(channel.idHex)
|
||||||
nav(Route.Message.route)
|
|
||||||
},
|
},
|
||||||
shape = ButtonBorder,
|
shape = ButtonBorder,
|
||||||
colors = ButtonDefaults
|
colors = ButtonDefaults
|
||||||
|
Reference in New Issue
Block a user