kopia lustrzana https://github.com/vitorpamplona/amethyst
770 wiersze
27 KiB
Kotlin
770 wiersze
27 KiB
Kotlin
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
|
|
|
import android.widget.Toast
|
|
import androidx.compose.animation.Crossfade
|
|
import androidx.compose.foundation.clickable
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.Spacer
|
|
import androidx.compose.foundation.layout.fillMaxHeight
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
import androidx.compose.foundation.layout.height
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.size
|
|
import androidx.compose.foundation.layout.width
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
import androidx.compose.foundation.lazy.itemsIndexed
|
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
import androidx.compose.foundation.rememberScrollState
|
|
import androidx.compose.foundation.text.KeyboardOptions
|
|
import androidx.compose.foundation.verticalScroll
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.filled.EditNote
|
|
import androidx.compose.material3.Button
|
|
import androidx.compose.material3.ButtonDefaults
|
|
import androidx.compose.material3.Divider
|
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
import androidx.compose.material3.Icon
|
|
import androidx.compose.material3.IconButton
|
|
import androidx.compose.material3.LocalTextStyle
|
|
import androidx.compose.material3.MaterialTheme
|
|
import androidx.compose.material3.OutlinedTextField
|
|
import androidx.compose.material3.Surface
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.material3.TextFieldDefaults
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.DisposableEffect
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
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.rememberCoroutineScope
|
|
import androidx.compose.runtime.setValue
|
|
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.platform.LocalLifecycleOwner
|
|
import androidx.compose.ui.res.painterResource
|
|
import androidx.compose.ui.res.stringResource
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
import androidx.compose.ui.text.input.KeyboardCapitalization
|
|
import androidx.compose.ui.text.input.TextFieldValue
|
|
import androidx.compose.ui.text.style.TextAlign
|
|
import androidx.compose.ui.text.style.TextDirection
|
|
import androidx.compose.ui.text.style.TextOverflow
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.window.Dialog
|
|
import androidx.compose.ui.window.DialogProperties
|
|
import androidx.lifecycle.Lifecycle
|
|
import androidx.lifecycle.LifecycleEventObserver
|
|
import androidx.lifecycle.distinctUntilChanged
|
|
import androidx.lifecycle.map
|
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
import com.vitorpamplona.amethyst.R
|
|
import com.vitorpamplona.amethyst.model.Note
|
|
import com.vitorpamplona.amethyst.model.ServersAvailable
|
|
import com.vitorpamplona.amethyst.model.User
|
|
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
|
|
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
|
import com.vitorpamplona.amethyst.ui.actions.NewPostViewModel
|
|
import com.vitorpamplona.amethyst.ui.actions.PostButton
|
|
import com.vitorpamplona.amethyst.ui.actions.UploadFromGallery
|
|
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
|
|
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
|
import com.vitorpamplona.amethyst.ui.note.DisplayRoomSubject
|
|
import com.vitorpamplona.amethyst.ui.note.DisplayUserSetAsSubject
|
|
import com.vitorpamplona.amethyst.ui.note.LoadUser
|
|
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
|
import com.vitorpamplona.amethyst.ui.note.QuickActionAlertDialog
|
|
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
|
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
|
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomFeedViewModel
|
|
import com.vitorpamplona.amethyst.ui.screen.RefreshingChatroomFeedView
|
|
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
|
import com.vitorpamplona.amethyst.ui.theme.EditFieldBorder
|
|
import com.vitorpamplona.amethyst.ui.theme.EditFieldModifier
|
|
import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier
|
|
import com.vitorpamplona.amethyst.ui.theme.Size30Modifier
|
|
import com.vitorpamplona.amethyst.ui.theme.Size34dp
|
|
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
|
import com.vitorpamplona.quartz.events.ChatMessageEvent
|
|
import com.vitorpamplona.quartz.events.ChatroomKey
|
|
import kotlinx.collections.immutable.persistentSetOf
|
|
import kotlinx.collections.immutable.toPersistentList
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.withContext
|
|
|
|
@Composable
|
|
fun ChatroomScreen(
|
|
roomId: String?,
|
|
draftMessage: String? = null,
|
|
accountViewModel: AccountViewModel,
|
|
nav: (String) -> Unit
|
|
) {
|
|
if (roomId == null) return
|
|
|
|
LoadRoom(roomId, accountViewModel) {
|
|
it?.let {
|
|
PrepareChatroomViewModels(
|
|
room = it,
|
|
draftMessage = draftMessage,
|
|
accountViewModel = accountViewModel,
|
|
nav = nav
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun ChatroomScreenByAuthor(
|
|
authorPubKeyHex: String?,
|
|
draftMessage: String? = null,
|
|
accountViewModel: AccountViewModel,
|
|
nav: (String) -> Unit
|
|
) {
|
|
if (authorPubKeyHex == null) return
|
|
|
|
LoadRoomByAuthor(authorPubKeyHex, accountViewModel) {
|
|
it?.let {
|
|
PrepareChatroomViewModels(
|
|
room = it,
|
|
draftMessage = draftMessage,
|
|
accountViewModel = accountViewModel,
|
|
nav = nav
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun LoadRoom(roomId: String, accountViewModel: AccountViewModel, content: @Composable (ChatroomKey?) -> Unit) {
|
|
var room by remember(roomId) {
|
|
mutableStateOf<ChatroomKey?>(null)
|
|
}
|
|
|
|
if (room == null) {
|
|
LaunchedEffect(key1 = roomId) {
|
|
launch(Dispatchers.IO) {
|
|
val newRoom = accountViewModel.userProfile().privateChatrooms.keys.firstOrNull { it.hashCode().toString() == roomId }
|
|
if (room != newRoom) {
|
|
room = newRoom
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
content(room)
|
|
}
|
|
|
|
@Composable
|
|
fun LoadRoomByAuthor(authorPubKeyHex: String, accountViewModel: AccountViewModel, content: @Composable (ChatroomKey?) -> Unit) {
|
|
val room by remember(authorPubKeyHex) {
|
|
mutableStateOf<ChatroomKey?>(ChatroomKey(persistentSetOf(authorPubKeyHex)))
|
|
}
|
|
|
|
content(room)
|
|
}
|
|
|
|
@Composable
|
|
fun PrepareChatroomViewModels(
|
|
room: ChatroomKey,
|
|
draftMessage: String?,
|
|
accountViewModel: AccountViewModel,
|
|
nav: (String) -> Unit
|
|
) {
|
|
val feedViewModel: NostrChatroomFeedViewModel = viewModel(
|
|
key = room.hashCode().toString() + "ChatroomViewModels",
|
|
factory = NostrChatroomFeedViewModel.Factory(
|
|
room,
|
|
accountViewModel.account
|
|
)
|
|
)
|
|
|
|
val newPostModel: NewPostViewModel = viewModel()
|
|
newPostModel.accountViewModel = accountViewModel
|
|
newPostModel.account = accountViewModel.account
|
|
newPostModel.requiresNIP24 = room.users.size > 1
|
|
if (newPostModel.requiresNIP24) {
|
|
newPostModel.nip24 = true
|
|
}
|
|
|
|
LaunchedEffect(key1 = newPostModel) {
|
|
launch(Dispatchers.IO) {
|
|
val hasNIP24 = accountViewModel.userProfile().privateChatrooms[room]?.roomMessages?.any {
|
|
it.event is ChatMessageEvent && (it.event as ChatMessageEvent).pubKey != accountViewModel.userProfile().pubkeyHex
|
|
}
|
|
if (hasNIP24 == true && newPostModel.nip24 == false) {
|
|
newPostModel.nip24 = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if (draftMessage != null) {
|
|
LaunchedEffect(key1 = draftMessage) {
|
|
newPostModel.message = TextFieldValue(draftMessage)
|
|
}
|
|
}
|
|
|
|
ChatroomScreen(
|
|
room = room,
|
|
feedViewModel = feedViewModel,
|
|
newPostModel = newPostModel,
|
|
accountViewModel = accountViewModel,
|
|
nav = nav
|
|
)
|
|
}
|
|
|
|
@Composable
|
|
fun ChatroomScreen(
|
|
room: ChatroomKey,
|
|
feedViewModel: NostrChatroomFeedViewModel,
|
|
newPostModel: NewPostViewModel,
|
|
accountViewModel: AccountViewModel,
|
|
nav: (String) -> Unit
|
|
) {
|
|
val context = LocalContext.current
|
|
|
|
NostrChatroomDataSource.loadMessagesBetween(accountViewModel.account, room)
|
|
|
|
val lifeCycleOwner = LocalLifecycleOwner.current
|
|
|
|
LaunchedEffect(room, accountViewModel) {
|
|
launch(Dispatchers.IO) {
|
|
newPostModel.imageUploadingError.collect { error ->
|
|
withContext(Dispatchers.Main) {
|
|
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DisposableEffect(room, accountViewModel) {
|
|
NostrChatroomDataSource.loadMessagesBetween(accountViewModel.account, room)
|
|
NostrChatroomDataSource.start()
|
|
feedViewModel.invalidateData()
|
|
|
|
onDispose {
|
|
NostrChatroomDataSource.stop()
|
|
}
|
|
}
|
|
|
|
DisposableEffect(lifeCycleOwner) {
|
|
val observer = LifecycleEventObserver { _, event ->
|
|
if (event == Lifecycle.Event.ON_RESUME) {
|
|
println("Private Message Start")
|
|
NostrChatroomDataSource.start()
|
|
feedViewModel.invalidateData()
|
|
}
|
|
if (event == Lifecycle.Event.ON_PAUSE) {
|
|
println("Private Message Stop")
|
|
NostrChatroomDataSource.stop()
|
|
}
|
|
}
|
|
|
|
lifeCycleOwner.lifecycle.addObserver(observer)
|
|
onDispose {
|
|
lifeCycleOwner.lifecycle.removeObserver(observer)
|
|
}
|
|
}
|
|
|
|
Column(Modifier.fillMaxHeight()) {
|
|
val replyTo = remember { mutableStateOf<Note?>(null) }
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxHeight()
|
|
.padding(vertical = 0.dp)
|
|
.weight(1f, true)
|
|
) {
|
|
RefreshingChatroomFeedView(
|
|
viewModel = feedViewModel,
|
|
accountViewModel = accountViewModel,
|
|
nav = nav,
|
|
routeForLastRead = "Room/${room.hashCode()}",
|
|
onWantsToReply = {
|
|
replyTo.value = it
|
|
}
|
|
)
|
|
}
|
|
|
|
Spacer(modifier = Modifier.height(10.dp))
|
|
|
|
replyTo.value?.let {
|
|
DisplayReplyingToNote(it, accountViewModel, nav) {
|
|
replyTo.value = null
|
|
}
|
|
}
|
|
|
|
val scope = rememberCoroutineScope()
|
|
|
|
// LAST ROW
|
|
PrivateMessageEditFieldRow(newPostModel, isPrivate = true, accountViewModel) {
|
|
scope.launch(Dispatchers.IO) {
|
|
if (newPostModel.nip24 || room.users.size > 1 || replyTo.value?.event is ChatMessageEvent) {
|
|
accountViewModel.account.sendNIP24PrivateMessage(
|
|
message = newPostModel.message.text,
|
|
toUsers = room.users.toList(),
|
|
replyingTo = replyTo.value,
|
|
mentions = null,
|
|
wantsToMarkAsSensitive = false
|
|
)
|
|
} else {
|
|
accountViewModel.account.sendPrivateMessage(
|
|
message = newPostModel.message.text,
|
|
toUser = room.users.first(),
|
|
replyingTo = replyTo.value,
|
|
mentions = null,
|
|
wantsToMarkAsSensitive = false
|
|
)
|
|
}
|
|
|
|
newPostModel.message = TextFieldValue("")
|
|
replyTo.value = null
|
|
feedViewModel.sendToTop()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
@Composable
|
|
fun PrivateMessageEditFieldRow(
|
|
channelScreenModel: NewPostViewModel,
|
|
isPrivate: Boolean,
|
|
accountViewModel: AccountViewModel,
|
|
onSendNewMessage: () -> Unit
|
|
) {
|
|
Row(
|
|
modifier = EditFieldModifier,
|
|
horizontalArrangement = Arrangement.SpaceBetween,
|
|
verticalAlignment = Alignment.CenterVertically
|
|
) {
|
|
val context = LocalContext.current
|
|
|
|
MyTextField(
|
|
value = channelScreenModel.message,
|
|
onValueChange = {
|
|
channelScreenModel.updateMessage(it)
|
|
},
|
|
keyboardOptions = KeyboardOptions.Default.copy(
|
|
capitalization = KeyboardCapitalization.Sentences
|
|
),
|
|
shape = EditFieldBorder,
|
|
modifier = Modifier.weight(1f, true),
|
|
placeholder = {
|
|
Text(
|
|
text = stringResource(R.string.reply_here),
|
|
color = MaterialTheme.colorScheme.placeholderText
|
|
)
|
|
},
|
|
trailingIcon = {
|
|
PostButton(
|
|
onPost = {
|
|
onSendNewMessage()
|
|
},
|
|
isActive = channelScreenModel.message.text.isNotBlank() && !channelScreenModel.isUploadingImage,
|
|
modifier = EditFieldTrailingIconModifier
|
|
)
|
|
},
|
|
leadingIcon = {
|
|
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 6.dp)) {
|
|
UploadFromGallery(
|
|
isUploading = channelScreenModel.isUploadingImage,
|
|
tint = MaterialTheme.colorScheme.placeholderText,
|
|
modifier = Modifier
|
|
.size(30.dp)
|
|
.padding(start = 2.dp)
|
|
) {
|
|
val fileServer = if (isPrivate) {
|
|
// TODO: Make private servers
|
|
when (accountViewModel.account.defaultFileServer) {
|
|
ServersAvailable.NOSTR_BUILD -> ServersAvailable.NOSTR_BUILD
|
|
ServersAvailable.NOSTRIMG -> ServersAvailable.NOSTRIMG
|
|
ServersAvailable.NOSTRFILES_DEV -> ServersAvailable.NOSTRFILES_DEV
|
|
ServersAvailable.NOSTRCHECK_ME -> ServersAvailable.NOSTRCHECK_ME
|
|
|
|
ServersAvailable.NOSTR_BUILD_NIP_94 -> ServersAvailable.NOSTR_BUILD
|
|
ServersAvailable.NOSTRIMG_NIP_94 -> ServersAvailable.NOSTRIMG
|
|
ServersAvailable.NOSTRFILES_DEV_NIP_94 -> ServersAvailable.NOSTRFILES_DEV
|
|
ServersAvailable.NOSTRCHECK_ME_NIP_94 -> ServersAvailable.NOSTRCHECK_ME
|
|
|
|
ServersAvailable.NIP95 -> ServersAvailable.NOSTR_BUILD
|
|
}
|
|
} else {
|
|
accountViewModel.account.defaultFileServer
|
|
}
|
|
|
|
channelScreenModel.upload(it, "", false, fileServer, context)
|
|
}
|
|
|
|
var wantsToActivateNIP24 by remember {
|
|
mutableStateOf(false)
|
|
}
|
|
|
|
if (wantsToActivateNIP24) {
|
|
NewFeatureNIP24AlertDialog(
|
|
accountViewModel = accountViewModel,
|
|
onConfirm = {
|
|
channelScreenModel.toggleNIP04And24()
|
|
},
|
|
onDismiss = {
|
|
wantsToActivateNIP24 = false
|
|
}
|
|
)
|
|
}
|
|
|
|
IconButton(
|
|
modifier = Size30Modifier,
|
|
onClick = {
|
|
if (!accountViewModel.hideNIP24WarningDialog && !channelScreenModel.nip24 && !channelScreenModel.requiresNIP24) {
|
|
wantsToActivateNIP24 = true
|
|
} else {
|
|
channelScreenModel.toggleNIP04And24()
|
|
}
|
|
}
|
|
) {
|
|
if (channelScreenModel.nip24) {
|
|
Icon(
|
|
painter = painterResource(id = R.drawable.incognito),
|
|
null,
|
|
modifier = Modifier
|
|
.padding(top = 2.dp)
|
|
.size(18.dp),
|
|
tint = MaterialTheme.colorScheme.primary
|
|
)
|
|
} else {
|
|
Icon(
|
|
painter = painterResource(id = R.drawable.incognito_off),
|
|
null,
|
|
modifier = Modifier
|
|
.padding(top = 2.dp)
|
|
.size(18.dp),
|
|
tint = MaterialTheme.colorScheme.placeholderText
|
|
)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
colors = TextFieldDefaults.colors(
|
|
focusedIndicatorColor = Color.Transparent,
|
|
unfocusedIndicatorColor = Color.Transparent
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun NewFeatureNIP24AlertDialog(accountViewModel: AccountViewModel, onConfirm: () -> Unit, onDismiss: () -> Unit) {
|
|
val scope = rememberCoroutineScope()
|
|
|
|
QuickActionAlertDialog(
|
|
title = stringResource(R.string.new_feature_nip24_might_not_be_available_title),
|
|
textContent = stringResource(R.string.new_feature_nip24_might_not_be_available_description),
|
|
buttonIconResource = R.drawable.incognito,
|
|
buttonText = stringResource(R.string.new_feature_nip24_activate),
|
|
onClickDoOnce = {
|
|
scope.launch(Dispatchers.IO) {
|
|
onConfirm()
|
|
}
|
|
onDismiss()
|
|
},
|
|
onClickDontShowAgain = {
|
|
scope.launch(Dispatchers.IO) {
|
|
onConfirm()
|
|
accountViewModel.dontShowNIP24WarningDialog()
|
|
}
|
|
onDismiss()
|
|
},
|
|
onDismiss = onDismiss
|
|
)
|
|
}
|
|
|
|
@Composable
|
|
fun ChatroomHeader(
|
|
room: ChatroomKey,
|
|
modifier: Modifier = StdPadding,
|
|
accountViewModel: AccountViewModel,
|
|
nav: (String) -> Unit
|
|
) {
|
|
if (room.users.size == 1) {
|
|
LoadUser(baseUserHex = room.users.first(), accountViewModel) { baseUser ->
|
|
if (baseUser != null) {
|
|
ChatroomHeader(baseUser = baseUser, modifier = modifier, accountViewModel = accountViewModel, nav = nav)
|
|
}
|
|
}
|
|
} else {
|
|
GroupChatroomHeader(room = room, modifier = modifier, accountViewModel = accountViewModel, nav = nav)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun ChatroomHeader(
|
|
baseUser: User,
|
|
modifier: Modifier = StdPadding,
|
|
accountViewModel: AccountViewModel,
|
|
nav: (String) -> Unit
|
|
) {
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.clickable(
|
|
onClick = { nav("User/${baseUser.pubkeyHex}") }
|
|
)
|
|
) {
|
|
Column(
|
|
verticalArrangement = Arrangement.Center,
|
|
modifier = modifier
|
|
) {
|
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
ClickableUserPicture(
|
|
baseUser = baseUser,
|
|
accountViewModel = accountViewModel,
|
|
size = Size34dp
|
|
)
|
|
|
|
Column(modifier = Modifier.padding(start = 10.dp)) {
|
|
UsernameDisplay(baseUser)
|
|
ObserveDisplayNip05Status(baseUser, accountViewModel = accountViewModel, nav = nav)
|
|
}
|
|
}
|
|
}
|
|
|
|
Divider(
|
|
thickness = 0.25.dp
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun GroupChatroomHeader(
|
|
room: ChatroomKey,
|
|
modifier: Modifier = StdPadding,
|
|
accountViewModel: AccountViewModel,
|
|
nav: (String) -> Unit
|
|
) {
|
|
val expanded = remember { mutableStateOf(false) }
|
|
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.clickable {
|
|
expanded.value = !expanded.value
|
|
}
|
|
) {
|
|
Column(
|
|
verticalArrangement = Arrangement.Center,
|
|
modifier = modifier
|
|
) {
|
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
NonClickableUserPictures(
|
|
users = room.users,
|
|
accountViewModel = accountViewModel,
|
|
size = Size34dp
|
|
)
|
|
|
|
Column(modifier = Modifier.padding(start = 10.dp)) {
|
|
RoomNameOnlyDisplay(room, Modifier, FontWeight.Bold, accountViewModel.userProfile())
|
|
DisplayUserSetAsSubject(room, accountViewModel, FontWeight.Normal)
|
|
}
|
|
}
|
|
|
|
if (expanded.value) {
|
|
LongRoomHeader(room = room, accountViewModel = accountViewModel, nav = nav)
|
|
}
|
|
}
|
|
|
|
Divider(
|
|
thickness = 0.25.dp
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun EditRoomSubjectButton(room: ChatroomKey, accountViewModel: AccountViewModel) {
|
|
var wantsToPost by remember {
|
|
mutableStateOf(false)
|
|
}
|
|
|
|
if (wantsToPost) {
|
|
NewSubjectView({ wantsToPost = false }, accountViewModel, room)
|
|
}
|
|
|
|
Button(
|
|
modifier = Modifier
|
|
.padding(horizontal = 3.dp)
|
|
.width(50.dp),
|
|
onClick = { wantsToPost = true },
|
|
shape = ButtonBorder,
|
|
colors = ButtonDefaults.buttonColors(
|
|
containerColor = MaterialTheme.colorScheme.primary
|
|
)
|
|
) {
|
|
Icon(
|
|
tint = Color.White,
|
|
imageVector = Icons.Default.EditNote,
|
|
contentDescription = stringResource(R.string.edits_the_channel_metadata)
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun NewSubjectView(onClose: () -> Unit, accountViewModel: AccountViewModel, room: ChatroomKey) {
|
|
Dialog(
|
|
onDismissRequest = { onClose() },
|
|
properties = DialogProperties(
|
|
dismissOnClickOutside = false
|
|
)
|
|
) {
|
|
Surface {
|
|
val groupName = remember {
|
|
mutableStateOf<String>(accountViewModel.userProfile().privateChatrooms[room]?.subject ?: "")
|
|
}
|
|
val message = remember {
|
|
mutableStateOf<String>("")
|
|
}
|
|
val scope = rememberCoroutineScope()
|
|
|
|
Column(
|
|
modifier = Modifier
|
|
.padding(10.dp)
|
|
.verticalScroll(rememberScrollState())
|
|
) {
|
|
Row(
|
|
modifier = Modifier
|
|
.fillMaxWidth(),
|
|
horizontalArrangement = Arrangement.SpaceBetween,
|
|
verticalAlignment = Alignment.CenterVertically
|
|
) {
|
|
CloseButton(onPress = {
|
|
onClose()
|
|
})
|
|
|
|
PostButton(
|
|
onPost = {
|
|
scope.launch(Dispatchers.IO) {
|
|
accountViewModel.account.sendNIP24PrivateMessage(
|
|
message = message.value,
|
|
toUsers = room.users.toList(),
|
|
subject = groupName.value.ifBlank { null },
|
|
replyingTo = null,
|
|
mentions = null,
|
|
wantsToMarkAsSensitive = false
|
|
)
|
|
}
|
|
|
|
onClose()
|
|
},
|
|
true
|
|
)
|
|
}
|
|
|
|
Spacer(modifier = Modifier.height(15.dp))
|
|
|
|
OutlinedTextField(
|
|
label = { Text(text = stringResource(R.string.messages_new_message_subject)) },
|
|
modifier = Modifier.fillMaxWidth(),
|
|
value = groupName.value,
|
|
onValueChange = { groupName.value = it },
|
|
placeholder = {
|
|
Text(
|
|
text = stringResource(R.string.messages_new_message_subject_caption),
|
|
color = MaterialTheme.colorScheme.placeholderText
|
|
)
|
|
},
|
|
keyboardOptions = KeyboardOptions.Default.copy(
|
|
capitalization = KeyboardCapitalization.Sentences
|
|
),
|
|
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
|
|
)
|
|
|
|
Spacer(modifier = Modifier.height(15.dp))
|
|
|
|
OutlinedTextField(
|
|
label = { Text(text = stringResource(R.string.messages_new_subject_message)) },
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.height(100.dp),
|
|
value = message.value,
|
|
onValueChange = { message.value = it },
|
|
placeholder = {
|
|
Text(
|
|
text = stringResource(R.string.messages_new_subject_message_placeholder),
|
|
color = MaterialTheme.colorScheme.placeholderText
|
|
)
|
|
},
|
|
keyboardOptions = KeyboardOptions.Default.copy(
|
|
capitalization = KeyboardCapitalization.Sentences
|
|
),
|
|
textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
|
|
maxLines = 10
|
|
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun LongRoomHeader(
|
|
room: ChatroomKey,
|
|
lineModifier: Modifier = StdPadding,
|
|
accountViewModel: AccountViewModel,
|
|
nav: (String) -> Unit
|
|
) {
|
|
val list = remember(room) {
|
|
room.users.toPersistentList()
|
|
}
|
|
|
|
Row(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
horizontalArrangement = Arrangement.Center,
|
|
verticalAlignment = Alignment.CenterVertically
|
|
) {
|
|
Text(
|
|
text = stringResource(id = R.string.messages_group_descriptor),
|
|
fontWeight = FontWeight.Bold,
|
|
maxLines = 1,
|
|
overflow = TextOverflow.Ellipsis,
|
|
modifier = Modifier.weight(1f),
|
|
textAlign = TextAlign.Center
|
|
)
|
|
|
|
EditRoomSubjectButton(room, accountViewModel)
|
|
}
|
|
|
|
LazyColumn(
|
|
modifier = Modifier,
|
|
state = rememberLazyListState()
|
|
) {
|
|
itemsIndexed(list, key = { _, item -> item }) { _, item ->
|
|
LoadUser(baseUserHex = item, accountViewModel) {
|
|
if (it != null) {
|
|
UserCompose(
|
|
baseUser = it,
|
|
overallModifier = lineModifier,
|
|
accountViewModel = accountViewModel,
|
|
nav = nav
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun RoomNameOnlyDisplay(room: ChatroomKey, modifier: Modifier, fontWeight: FontWeight = FontWeight.Bold, loggedInUser: User) {
|
|
val roomSubject by loggedInUser.live().messages.map {
|
|
it.user.privateChatrooms[room]?.subject
|
|
}.distinctUntilChanged().observeAsState(loggedInUser.privateChatrooms[room]?.subject)
|
|
|
|
Crossfade(targetState = roomSubject, modifier) {
|
|
if (it != null && it.isNotBlank()) {
|
|
DisplayRoomSubject(it, fontWeight)
|
|
}
|
|
}
|
|
}
|