diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt index 80b356b1f..c988e6cbd 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt @@ -1,5 +1,6 @@ package com.vitorpamplona.amethyst.model +import androidx.compose.runtime.Stable import androidx.lifecycle.LiveData import com.vitorpamplona.amethyst.service.NostrSingleChannelDataSource import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent @@ -9,6 +10,7 @@ import fr.acinq.secp256k1.Hex import kotlinx.coroutines.Dispatchers import java.util.concurrent.ConcurrentHashMap +@Stable class Channel(val idHex: String) { var creator: User? = null var info = ChannelCreateEvent.ChannelData(null, null, null) @@ -33,6 +35,10 @@ class Channel(val idHex: String) { notes.remove(note.idHex) } + fun removeNote(noteHex: String) { + notes.remove(noteHex) + } + fun updateChannelInfo(creator: User, channelInfo: ChannelCreateEvent.ChannelData, updatedAt: Long) { this.creator = creator this.info = channelInfo diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt index 1d1842bc7..d1059e3ea 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt @@ -429,8 +429,10 @@ object LocalCache { masterNote.removeReport(deleteNote) } - val channel = deleteNote.channel() - channel?.removeNote(deleteNote) + deleteNote.channelHex()?.let { + val channel = checkGetOrCreateChannel(it) + channel?.removeNote(deleteNote) + } if (deleteNote.event is PrivateDmEvent) { val author = deleteNote.author diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt index 8f401f3b4..27645d425 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt @@ -73,10 +73,6 @@ open class Note(val idHex: String) { ?: (event as? ChannelCreateEvent)?.id } - fun channel(): Channel? { - return channelHex()?.let { LocalCache.checkGetOrCreateChannel(it) } - } - open fun address(): ATag? = null open fun createdAt() = event?.createdAt() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ChannelMessageEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ChannelMessageEvent.kt index f953c3045..09cacffc4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ChannelMessageEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ChannelMessageEvent.kt @@ -16,7 +16,12 @@ class ChannelMessageEvent( sig: HexKey ) : BaseTextNoteEvent(id, pubKey, createdAt, kind, tags, content, sig) { - fun channel() = tags.firstOrNull { it[0] == "e" && it.size > 3 && it[3] == "root" }?.getOrNull(1) ?: tags.firstOrNull { it.firstOrNull() == "e" }?.getOrNull(1) + fun channel() = tags.firstOrNull { + it.size > 3 && it[0] == "e" && it[3] == "root" + }?.get(1) ?: tags.firstOrNull { + it.size > 1 && it[0] == "e" + }?.get(1) + override fun replyTos() = tags.filter { it.firstOrNull() == "e" && it.getOrNull(1) != channel() }.mapNotNull { it.getOrNull(1) } companion object { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ChannelMetadataEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ChannelMetadataEvent.kt index b3db7553e..6f188f823 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ChannelMetadataEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ChannelMetadataEvent.kt @@ -16,7 +16,7 @@ class ChannelMetadataEvent( content: String, sig: HexKey ) : Event(id, pubKey, createdAt, kind, tags, content, sig) { - fun channel() = tags.firstOrNull { it.firstOrNull() == "e" }?.getOrNull(1) + fun channel() = tags.firstOrNull { it.size > 1 && it[0] == "e" }?.get(1) fun channelInfo() = try { MetadataEvent.gson.fromJson(content, ChannelCreateEvent.ChannelData::class.java) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ContactListEvent.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ContactListEvent.kt index b10099073..cbf3c834c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/model/ContactListEvent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/model/ContactListEvent.kt @@ -23,14 +23,12 @@ class ContactListEvent( // This function is only used by the user logged in // But it is used all the time. val verifiedFollowKeySet: Set by lazy { - tags.filter { it[0] == "p" }.mapNotNull { - it.getOrNull(1)?.let { unverifiedHex: String -> - try { - decodePublicKey(unverifiedHex).toHexKey() - } catch (e: Exception) { - Log.w("ContactListEvent", "Can't parse tags as a follows: ${it[1]}", e) - null - } + tags.filter { it.size > 1 && it[0] == "p" }.mapNotNull { + try { + decodePublicKey(it[1]).toHexKey() + } catch (e: Exception) { + Log.w("ContactListEvent", "Can't parse tags as a follows: ${it[1]}", e) + null } }.toSet() } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt index 55b5e04e8..00ffcd3f0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMessageTagger.kt @@ -1,13 +1,12 @@ package com.vitorpamplona.amethyst.ui.actions -import com.vitorpamplona.amethyst.model.Channel import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.parseDirtyWordForKey import com.vitorpamplona.amethyst.service.nip19.Nip19 -class NewMessageTagger(var channel: Channel?, var mentions: List?, var replyTos: List?, var message: String) { +class NewMessageTagger(var channelHex: String?, var mentions: List?, var replyTos: List?, var message: String) { fun addUserToMentions(user: User) { mentions = if (mentions?.contains(user) == true) mentions else mentions?.plus(user) ?: listOf(user) @@ -20,12 +19,12 @@ class NewMessageTagger(var channel: Channel?, var mentions: List?, var rep fun tagIndex(user: User): Int { // Postr Events assembles replies before mentions in the tag order - return (if (channel != null) 1 else 0) + (replyTos?.size ?: 0) + (mentions?.indexOf(user) ?: 0) + return (if (channelHex != null) 1 else 0) + (replyTos?.size ?: 0) + (mentions?.indexOf(user) ?: 0) } fun tagIndex(note: Note): Int { // Postr Events assembles replies before mentions in the tag order - return (if (channel != null) 1 else 0) + (replyTos?.indexOf(note) ?: 0) + return (if (channelHex != null) 1 else 0) + (replyTos?.indexOf(note) ?: 0) } fun run() { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollView.kt deleted file mode 100644 index c7f19229c..000000000 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPollView.kt +++ /dev/null @@ -1,197 +0,0 @@ -package com.vitorpamplona.amethyst.ui.actions - -import android.widget.Toast -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -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.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.Dialog -import androidx.compose.ui.window.DialogProperties -import androidx.lifecycle.viewmodel.compose.viewModel -import com.vitorpamplona.amethyst.R -import com.vitorpamplona.amethyst.model.Note -import com.vitorpamplona.amethyst.service.model.TextNoteEvent -import com.vitorpamplona.amethyst.ui.components.* -import com.vitorpamplona.amethyst.ui.note.ReplyInformation -import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.UserLine -import kotlinx.coroutines.delay - -@Composable -fun NewPollView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, accountViewModel: AccountViewModel) { - val pollViewModel: NewPostViewModel = viewModel() - - val context = LocalContext.current - - val scrollState = rememberScrollState() - - LaunchedEffect(Unit) { - pollViewModel.load(accountViewModel.account, baseReplyTo, quote) - delay(100) - - pollViewModel.imageUploadingError.collect { error -> - Toast.makeText(context, error, Toast.LENGTH_SHORT).show() - } - } - - Dialog( - onDismissRequest = { onClose() }, - properties = DialogProperties( - usePlatformDefaultWidth = false, - dismissOnClickOutside = false, - decorFitsSystemWindows = false - ) - ) { - Surface( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight() - ) { - Column( - modifier = Modifier - .padding(start = 10.dp, end = 10.dp, top = 10.dp) - .imePadding() - .weight(1f) - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - CloseButton(onCancel = { - pollViewModel.cancel() - onClose() - }) - - PollButton( - onPost = { - pollViewModel.sendPost() - onClose() - }, - isActive = pollViewModel.message.text.isNotBlank() && - pollViewModel.pollOptions.values.all { it.isNotEmpty() } && - pollViewModel.isValidRecipients.value && - pollViewModel.isValidvalueMaximum.value && - pollViewModel.isValidvalueMinimum.value && - pollViewModel.isValidConsensusThreshold.value && - pollViewModel.isValidClosedAt.value - ) - } - - Row( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .verticalScroll(scrollState) - ) { - if (pollViewModel.replyTos != null && baseReplyTo?.event is TextNoteEvent) { - ReplyInformation(pollViewModel.replyTos, pollViewModel.mentions, accountViewModel, "✖ ") { - pollViewModel.removeFromReplyList(it) - } - } - - Text(stringResource(R.string.poll_heading_required)) - // NewPollRecipientsField(pollViewModel, account) - NewPollPrimaryDescription(pollViewModel) - pollViewModel.pollOptions.values.forEachIndexed { index, _ -> - NewPollOption(pollViewModel, index) - } - Button( - onClick = { pollViewModel.pollOptions[pollViewModel.pollOptions.size] = "" }, - border = BorderStroke(1.dp, MaterialTheme.colors.onSurface.copy(alpha = 0.32f)), - colors = ButtonDefaults.outlinedButtonColors( - contentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) - ) - ) { - Image( - painterResource(id = android.R.drawable.ic_input_add), - contentDescription = "Add poll option button", - modifier = Modifier.size(18.dp) - ) - } - Text(stringResource(R.string.poll_heading_optional)) - NewPollVoteValueRange(pollViewModel) - NewPollConsensusThreshold(pollViewModel) - NewPollClosing(pollViewModel) - } - } - - val userSuggestions = pollViewModel.userSuggestions - if (userSuggestions.isNotEmpty()) { - LazyColumn( - contentPadding = PaddingValues( - top = 10.dp - ), - modifier = Modifier.heightIn(0.dp, 300.dp) - ) { - itemsIndexed( - userSuggestions, - key = { _, item -> item.pubkeyHex } - ) { _, item -> - UserLine(item, accountViewModel) { - pollViewModel.autocompleteWithUser(item) - } - } - } - } - - Row(modifier = Modifier.fillMaxWidth()) { - /*UploadFromGallery( - isUploading = pollViewModel.isUploadingImage - ) { - pollViewModel.upload(it, context) - }*/ - } - } - } - } - } -} - -@Composable -fun PollButton(modifier: Modifier = Modifier, onPost: () -> Unit = {}, isActive: Boolean) { - Button( - modifier = modifier, - onClick = { - if (isActive) { - onPost() - } - }, - shape = RoundedCornerShape(20.dp), - colors = ButtonDefaults - .buttonColors( - backgroundColor = if (isActive) MaterialTheme.colors.primary else Color.Gray - ) - ) { - Text(text = stringResource(R.string.post_poll), color = Color.White) - } -} - -/*@Preview -@Composable -fun NewPollViewPreview() { - NewPollView(onClose = {}, account = Account(loggedIn = Persona())) -}*/ diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt index 17d792a61..ba690b86f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostViewModel.kt @@ -102,7 +102,7 @@ open class NewPostViewModel : ViewModel() { } canAddInvoice = account.userProfile().info?.lnAddress() != null - canUsePoll = originalNote?.event !is PrivateDmEvent && originalNote?.channel() == null + canUsePoll = originalNote?.event !is PrivateDmEvent && originalNote?.channelHex() == null contentToAddUrl = null wantsForwardZapTo = false @@ -114,7 +114,7 @@ open class NewPostViewModel : ViewModel() { } fun sendPost() { - val tagger = NewMessageTagger(originalNote?.channel(), mentions, replyTos, message.text) + val tagger = NewMessageTagger(originalNote?.channelHex(), mentions, replyTos, message.text) tagger.run() val zapReceiver = if (wantsForwardZapTo) { @@ -129,8 +129,8 @@ open class NewPostViewModel : ViewModel() { if (wantsPoll) { account?.sendPoll(tagger.message, tagger.replyTos, tagger.mentions, pollOptions, valueMaximum, valueMinimum, consensusThreshold, closedAt, zapReceiver, wantsToMarkAsSensitive) - } else if (originalNote?.channel() != null) { - account?.sendChannelMessage(tagger.message, tagger.channel!!.idHex, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive) + } else if (originalNote?.channelHex() != null) { + account?.sendChannelMessage(tagger.message, tagger.channelHex!!, tagger.replyTos, tagger.mentions, zapReceiver, wantsToMarkAsSensitive) } else if (originalNote?.event is PrivateDmEvent) { account?.sendPrivateMessage(tagger.message, originalNote!!.author!!.pubkeyHex, originalNote!!, tagger.mentions, zapReceiver, wantsToMarkAsSensitive) } else { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt index 3a963cab0..fe49bce6c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt @@ -14,6 +14,7 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf @@ -45,9 +46,9 @@ import com.vitorpamplona.amethyst.service.NIP30Parser import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.PrivateDmEvent import com.vitorpamplona.amethyst.service.nip19.Nip19 +import com.vitorpamplona.amethyst.ui.note.LoadChannel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext @Composable fun ClickableRoute( @@ -77,40 +78,52 @@ private fun DisplayEvent( var noteBase by remember(nip19) { mutableStateOf(null) } LaunchedEffect(key1 = nip19.hex) { - withContext(Dispatchers.IO) { - noteBase = LocalCache.checkGetOrCreateNote(nip19.hex) + if (noteBase == null) { + launch(Dispatchers.IO) { + noteBase = LocalCache.checkGetOrCreateNote(nip19.hex) + } } } noteBase?.let { val noteState by it.live().metadata.observeAsState() val note = remember(noteState) { noteState?.note } ?: return - val channel = remember(noteState) { note.channel() } + val channelHex = remember(noteState) { note.channelHex() } + val noteIdDisplayNote = remember(noteState) { "@${note.idDisplayNote()}" } if (note.event is ChannelCreateEvent) { CreateClickableText( - clickablePart = "@${note.idDisplayNote()}", + clickablePart = noteIdDisplayNote, suffix = "${nip19.additionalChars} ", route = "Channel/${nip19.hex}", nav = nav ) } else if (note.event is PrivateDmEvent) { CreateClickableText( - clickablePart = "@${note.idDisplayNote()}", + clickablePart = noteIdDisplayNote, suffix = "${nip19.additionalChars} ", route = "Room/${note.author?.pubkeyHex}", nav = nav ) - } else if (channel != null) { - CreateClickableText( - clickablePart = channel.toBestDisplayName(), - suffix = "${nip19.additionalChars} ", - route = "Channel/${channel.idHex}", - nav = nav - ) + } else if (channelHex != null) { + LoadChannel(baseChannelHex = channelHex) { baseChannel -> + val channelState by baseChannel.live.observeAsState() + val channelDisplayName by remember(channelState) { + derivedStateOf { + channelState?.channel?.toBestDisplayName() ?: noteIdDisplayNote + } + } + + CreateClickableText( + clickablePart = channelDisplayName, + suffix = "${nip19.additionalChars} ", + route = "Channel/${baseChannel.idHex}", + nav = nav + ) + } } else { CreateClickableText( - clickablePart = "@${note.idDisplayNote()}", + clickablePart = noteIdDisplayNote, suffix = "${nip19.additionalChars} ", route = "Event/${nip19.hex}", nav = nav @@ -141,32 +154,42 @@ private fun DisplayNote( noteBase?.let { val noteState by it.live().metadata.observeAsState() val note = remember(noteState) { noteState?.note } ?: return - val channel = note.channel() + val channelHex = note.channelHex() + val noteIdDisplayNote = remember(noteState) { "@${note.idDisplayNote()}" } if (note.event is ChannelCreateEvent) { CreateClickableText( - clickablePart = "@${note.idDisplayNote()}", + clickablePart = noteIdDisplayNote, suffix = "${nip19.additionalChars} ", route = "Channel/${nip19.hex}", nav = nav ) } else if (note.event is PrivateDmEvent) { CreateClickableText( - clickablePart = "@${note.idDisplayNote()}", + clickablePart = noteIdDisplayNote, suffix = "${nip19.additionalChars} ", route = "Room/${note.author?.pubkeyHex}", nav = nav ) - } else if (channel != null) { - CreateClickableText( - clickablePart = channel.toBestDisplayName(), - suffix = "${nip19.additionalChars} ", - route = "Channel/${note.channel()?.idHex}", - nav = nav - ) + } else if (channelHex != null) { + LoadChannel(baseChannelHex = channelHex) { baseChannel -> + val channelState by baseChannel.live.observeAsState() + val channelDisplayName by remember(channelState) { + derivedStateOf { + channelState?.channel?.toBestDisplayName() ?: noteIdDisplayNote + } + } + + CreateClickableText( + clickablePart = channelDisplayName, + suffix = "${nip19.additionalChars} ", + route = "Channel/${baseChannel.idHex}", + nav = nav + ) + } } else { CreateClickableText( - clickablePart = "@${note.idDisplayNote()}", + clickablePart = noteIdDisplayNote, suffix = "${nip19.additionalChars} ", route = "Note/${nip19.hex}", nav = nav @@ -434,7 +457,9 @@ fun ClickableInLineIconRenderer(wordsInOrder: List, style: SpanStyle AsyncImage( model = value.url, contentDescription = null, - modifier = Modifier.fillMaxSize().padding(1.dp) + modifier = Modifier + .fillMaxSize() + .padding(1.dp) ) } ) @@ -498,7 +523,9 @@ fun InLineIconRenderer( AsyncImage( model = value.url, contentDescription = null, - modifier = Modifier.fillMaxSize().padding(horizontal = 1.dp) + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 1.dp) ) } ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt index c1b94d455..49c122280 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt @@ -228,7 +228,6 @@ fun AppNavigation( composable(route.route, route.arguments, content = { LoadRedirectScreen( eventId = it.arguments?.getString("id"), - accountViewModel = accountViewModel, navController = navController ) }) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt index 4109d5123..3663c9084 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt @@ -18,6 +18,7 @@ import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf @@ -66,39 +67,59 @@ fun ChatroomCompose( val scope = rememberCoroutineScope() + val channelHex by remember(noteState) { + derivedStateOf { + noteState?.note?.channelHex() + } + } + if (note?.event == null) { BlankNote(Modifier) - } else if (note.channel() != null) { - val authorState by note.author!!.live().metadata.observeAsState() - val author = authorState?.user + } else if (channelHex != null) { + LoadChannel(baseChannelHex = channelHex!!) { channel -> + val authorState by note.author!!.live().metadata.observeAsState() + val authorName = remember(authorState) { + authorState?.user?.toBestDisplayName() + } - val channelState by note.channel()!!.live.observeAsState() - val channel = channelState?.channel + val chanHex = remember { channel.idHex } - val noteEvent = note.event + val channelState by channel.live.observeAsState() + val channelPicture by remember(channelState) { + derivedStateOf { + channel.profilePicture() + } + } + val channelName by remember(channelState) { + derivedStateOf { + channel.info.name + } + } + + val noteEvent = note.event + + val description = if (noteEvent is ChannelCreateEvent) { + stringResource(R.string.channel_created) + } else if (noteEvent is ChannelMetadataEvent) { + "${stringResource(R.string.channel_information_changed_to)} " + } else { + noteEvent?.content() + } - val description = if (noteEvent is ChannelCreateEvent) { - stringResource(R.string.channel_created) - } else if (noteEvent is ChannelMetadataEvent) { - "${stringResource(R.string.channel_information_changed_to)} " - } else { - noteEvent?.content() - } - channel?.let { chan -> var hasNewMessages by remember { mutableStateOf(false) } LaunchedEffect(key1 = notificationCache, key2 = note) { scope.launch(Dispatchers.IO) { note.createdAt()?.let { timestamp -> hasNewMessages = - timestamp > notificationCache.cache.load("Channel/${chan.idHex}") + timestamp > notificationCache.cache.load("Channel/$chanHex") } } } ChannelName( - channelIdHex = chan.idHex, - channelPicture = chan.profilePicture(), + channelIdHex = chanHex, + channelPicture = channelPicture, channelTitle = { modifier -> Text( text = buildAnnotatedString { @@ -107,7 +128,7 @@ fun ChatroomCompose( fontWeight = FontWeight.Bold ) ) { - append(chan.info.name) + append(channelName) } withStyle( @@ -125,9 +146,9 @@ fun ChatroomCompose( ) }, channelLastTime = note.createdAt(), - channelLastContent = "${author?.toBestDisplayName()}: " + description, + channelLastContent = "$authorName: $description", hasNewMessages = hasNewMessages, - onClick = { nav("Channel/${chan.idHex}") } + onClick = { nav("Channel/$chanHex") } ) } } else { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt index a2afe2148..05677006a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt @@ -115,7 +115,7 @@ fun ChatroomMessageCompose( var isAcceptableAndCanPreview by remember { mutableStateOf(Pair(true, true)) } LaunchedEffect(key1 = noteReportsState, key2 = accountState) { - withContext(Dispatchers.IO) { + launch(Dispatchers.Default) { account.userProfile().let { loggedIn -> val newCanPreview = note.author?.pubkeyHex == loggedIn.pubkeyHex || (note.author?.let { loggedIn.isFollowingCached(it) } ?: true) || diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 9b223175a..af8311672 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -138,6 +138,8 @@ import java.io.File import java.math.BigDecimal import java.net.URL import java.util.Locale +import kotlin.time.ExperimentalTime +import kotlin.time.measureTimedValue @OptIn(ExperimentalFoundationApi::class) @Composable @@ -154,10 +156,12 @@ fun NoteCompose( accountViewModel: AccountViewModel, nav: (String) -> Unit ) { + println("NormalNote START NoteCompose") val noteState by baseNote.live().metadata.observeAsState() val noteEvent = remember(noteState) { noteState?.note?.event } if (noteEvent == null) { + println("NormalNote Rendering Blank") var popupExpanded by remember { mutableStateOf(false) } BlankNote( @@ -186,6 +190,7 @@ fun NoteCompose( nav ) } + println("NormalNote END NoteCompose") } @Composable @@ -251,32 +256,24 @@ fun LoadedNoteCompose( accountViewModel: AccountViewModel, nav: (String) -> Unit ) { - val accountState by accountViewModel.accountLiveData.observeAsState() - val account = remember(accountState) { accountState?.account } ?: return + var state by remember { + mutableStateOf( + NoteComposeReportState( + isAcceptable = true, + canPreview = true, + relevantReports = emptySet() + ) + ) + } - val noteReportsState by note.live().reports.observeAsState() - val noteForReports = remember(noteReportsState) { noteReportsState?.note } ?: return - - var showReportedNote by remember { mutableStateOf(false) } - var state by remember { mutableStateOf(NoteComposeReportState(true, true, emptySet())) } - - LaunchedEffect(key1 = noteReportsState, key2 = accountState) { - withContext(Dispatchers.IO) { - account.userProfile().let { loggedIn -> - val newCanPreview = note.author?.pubkeyHex == loggedIn.pubkeyHex || - (note.author?.let { loggedIn.isFollowingCached(it) } ?: true) || - !(noteForReports.hasAnyReports()) - - val newIsAcceptable = account.isAcceptable(noteForReports) - val newRelevantReports = account.getRelevantReports(noteForReports) - - if (newIsAcceptable != state.isAcceptable || newCanPreview != state.canPreview) { - state = NoteComposeReportState(newIsAcceptable, newCanPreview, newRelevantReports) - } - } + WatchForReports(note, accountViewModel) { newIsAcceptable, newCanPreview, newRelevantReports -> + if (newIsAcceptable != state.isAcceptable || newCanPreview != state.canPreview) { + state = NoteComposeReportState(newIsAcceptable, newCanPreview, newRelevantReports) } } + var showReportedNote by remember { mutableStateOf(false) } + val showHiddenNote by remember(state, showReportedNote) { derivedStateOf { !state.isAcceptable && !showReportedNote @@ -316,7 +313,35 @@ fun LoadedNoteCompose( } } -@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun WatchForReports( + note: Note, + accountViewModel: AccountViewModel, + onChange: (Boolean, Boolean, Set) -> Unit +) { + val accountState by accountViewModel.accountLiveData.observeAsState() + val account = remember(accountState) { accountState?.account } ?: return + + val noteReportsState by note.live().reports.observeAsState() + val noteForReports = remember(noteReportsState) { noteReportsState?.note } ?: return + + LaunchedEffect(key1 = noteReportsState, key2 = accountState) { + launch(Dispatchers.Default) { + account.userProfile().let { loggedIn -> + val newCanPreview = note.author?.pubkeyHex == loggedIn.pubkeyHex || + (note.author?.let { loggedIn.isFollowingCached(it) } ?: true) || + !(noteForReports.hasAnyReports()) + + val newIsAcceptable = account.isAcceptable(noteForReports) + val newRelevantReports = account.getRelevantReports(noteForReports) + + onChange(newIsAcceptable, newCanPreview, newRelevantReports) + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class, ExperimentalTime::class) @Composable fun NormalNote( baseNote: Note, @@ -346,6 +371,8 @@ fun NormalNote( } else if (noteEvent is FileStorageHeaderEvent) { FileStorageHeaderDisplay(baseNote) } else { + println("NormalNote LoadedNoteCompose") + var isNew by remember { mutableStateOf(false) } val scope = rememberCoroutineScope() @@ -368,6 +395,8 @@ fun NormalNote( } } + println("NormalNote LoadedNoteCompose Launched Effect") + val primaryColor = MaterialTheme.colors.newItemBackgroundColor val defaultBackgroundColor = MaterialTheme.colors.background @@ -383,6 +412,8 @@ fun NormalNote( } } + println("NormalNote LoadedNoteCompose Background Color") + val columnModifier = remember(backgroundColor) { modifier .combinedClickable( @@ -398,6 +429,8 @@ fun NormalNote( .background(backgroundColor) } + println("NormalNote LoadedNoteCompose Modifier") + Column(modifier = columnModifier) { Row( modifier = remember { @@ -409,9 +442,14 @@ fun NormalNote( ) } ) { - if (!isBoostedNote && !isQuotedNote) { - DrawAuthorImages(baseNote, accountViewModel, nav) + println("NormalNote Before FirstUserInfo") + val (value, elapsed) = measureTimedValue { + if (!isBoostedNote && !isQuotedNote) { + DrawAuthorImages(baseNote, accountViewModel, nav) + } } + println("AAA $elapsed DrawAuthorImages") + println("NormalNote FirstUserInfo") Column( modifier = remember { @@ -433,6 +471,7 @@ fun NormalNote( nav ) } + println("NormalNote SecondUserInfo") Spacer(modifier = Modifier.height(2.dp)) @@ -521,8 +560,8 @@ fun routeFor(note: Note, loggedIn: User): String? { val noteEvent = note.event if (noteEvent is ChannelMessageEvent || noteEvent is ChannelCreateEvent || noteEvent is ChannelMetadataEvent) { - note.channel()?.let { - return "Channel/${it.idHex}" + note.channelHex()?.let { + return "Channel/$it" } } else if (noteEvent is PrivateDmEvent) { return "Room/${noteEvent.talkingWith(loggedIn.pubkeyHex)}" @@ -546,6 +585,7 @@ private fun RenderTextEvent( if (eventContent != null) { val isAuthorTheLoggedUser = remember(note.event) { accountViewModel.isLoggedUser(note.author) } + if (makeItShort && isAuthorTheLoggedUser) { Text( text = eventContent, @@ -1232,7 +1272,7 @@ private fun ReplyRow( Spacer(modifier = Modifier.height(5.dp)) } else if (noteEvent is ChannelMessageEvent && (note.replyTo != null || noteEvent.hasAnyTaggedUser())) { - note.channel()?.let { + note.channelHex()?.let { ReplyInformationChannel(note.replyTo, noteEvent.mentions(), it, accountViewModel, nav) } @@ -1341,23 +1381,36 @@ fun TimeAgo(time: Long) { ) } +@OptIn(ExperimentalTime::class) @Composable private fun DrawAuthorImages(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) { - val baseChannel = remember { baseNote.channel() } + val baseChannelHex = remember { baseNote.channelHex() } val modifier = remember { Modifier.width(55.dp) } Column(modifier) { // Draws the boosted picture outside the boosted card. Box(modifier = modifier, contentAlignment = Alignment.BottomEnd) { - NoteAuthorPicture(baseNote, nav, accountViewModel, 55.dp) - - if (baseNote.event is RepostEvent) { - RepostNoteAuthorPicture(baseNote, accountViewModel, nav) + val (value1, elapsed1) = measureTimedValue { + NoteAuthorPicture(baseNote, nav, accountViewModel, 55.dp) } - if (baseNote.event is ChannelMessageEvent && baseChannel != null) { - ChannelNotePicture(baseChannel) + println("AAA $elapsed1 NoteAuthorPicture") + + val (value2, elapsed2) = measureTimedValue { + if (baseNote.event is RepostEvent) { + RepostNoteAuthorPicture(baseNote, accountViewModel, nav) + } } + println("AAA $elapsed2 RepostNoteAuthorPicture") + + val (value3, elapsed3) = measureTimedValue { + if (baseNote.event is ChannelMessageEvent && baseChannelHex != null) { + LoadChannel(baseChannelHex) { channel -> + ChannelNotePicture(channel) + } + } + } + println("AAA $elapsed3 ChannelNotePicture") } if (baseNote.event is RepostEvent) { @@ -1373,10 +1426,29 @@ private fun DrawAuthorImages(baseNote: Note, accountViewModel: AccountViewModel, } } +@Composable +fun LoadChannel(baseChannelHex: String, content: @Composable (Channel) -> Unit) { + var channel by remember(baseChannelHex) { + mutableStateOf(null) + } + + LaunchedEffect(key1 = baseChannelHex) { + if (channel == null) { + launch(Dispatchers.IO) { + channel = LocalCache.checkGetOrCreateChannel(baseChannelHex) + } + } + } + + channel?.let { + content(it) + } +} + @Composable private fun ChannelNotePicture(baseChannel: Channel) { val channelState by baseChannel.live.observeAsState() - val channel = channelState?.channel + val channel = remember(channelState) { channelState?.channel } ?: return val modifier = remember { Modifier @@ -1391,25 +1463,23 @@ private fun ChannelNotePicture(baseChannel: Channel) { .height(30.dp) } - if (channel != null) { - val model = remember(channelState) { - ResizeImage(channel.profilePicture(), 30.dp) - } + val model = remember(channelState) { + ResizeImage(channel.profilePicture(), 30.dp) + } - Box(boxModifier) { - RobohashAsyncImageProxy( - robot = channel.idHex, - model = model, - contentDescription = stringResource(R.string.group_picture), - modifier = modifier - .background(MaterialTheme.colors.background) - .border( - 2.dp, - MaterialTheme.colors.background, - CircleShape - ) - ) - } + Box(boxModifier) { + RobohashAsyncImageProxy( + robot = channel.idHex, + model = model, + contentDescription = stringResource(R.string.group_picture), + modifier = modifier + .background(MaterialTheme.colors.background) + .border( + 2.dp, + MaterialTheme.colors.background, + CircleShape + ) + ) } } @@ -2153,8 +2223,10 @@ fun NoteAuthorPicture( onClick: ((User) -> Unit)? = null ) { val noteState by baseNote.live().metadata.observeAsState() - val author = remember(noteState) { - noteState?.note?.author + val author by remember(noteState) { + derivedStateOf { + noteState?.note?.author + } } val boxModifier = remember { @@ -2179,7 +2251,7 @@ fun NoteAuthorPicture( modifier = nullModifier.background(MaterialTheme.colors.background) ) } else { - UserPicture(author, size, accountViewModel, modifier, onClick) + UserPicture(author!!, size, accountViewModel, modifier, onClick) } } } @@ -2253,15 +2325,12 @@ fun UserPicture( accountViewModel: AccountViewModel ) { val myBoxModifier = remember { - Modifier - .width(size) - .height(size) + Modifier.size(size) } val myImageModifier = remember { modifier - .width(size) - .height(size) + .size(size) .clip(shape = CircleShape) } @@ -2283,10 +2352,17 @@ fun UserPicture( private fun ObserveAndDisplayFollowingMark(userHex: String, iconSize: Dp, accountViewModel: AccountViewModel) { val accountFollowsState by accountViewModel.account.userProfile().live().follows.observeAsState() - val showFollowingMark by remember(accountFollowsState) { - derivedStateOf { - accountFollowsState?.user?.isFollowingCached(userHex) == true || - (userHex == accountViewModel.account.userProfile().pubkeyHex) + var showFollowingMark by remember { mutableStateOf(false) } + + LaunchedEffect(key1 = accountFollowsState) { + launch(Dispatchers.Default) { + val newShowFollowingMark = + accountFollowsState?.user?.isFollowingCached(userHex) == true || + (userHex == accountViewModel.account.userProfile().pubkeyHex) + + if (newShowFollowingMark != showFollowingMark) { + showFollowingMark = newShowFollowingMark + } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt index 2c9ab94bb..c7cfdfe2d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteQuickActionMenu.kt @@ -111,33 +111,34 @@ private fun VerticalDivider(color: Color) = @Composable fun NoteQuickActionMenu(note: Note, popupExpanded: Boolean, onDismiss: () -> Unit, accountViewModel: AccountViewModel) { - val context = LocalContext.current - val primaryLight = lightenColor(MaterialTheme.colors.primary, 0.1f) - val cardShape = RoundedCornerShape(5.dp) - val clipboardManager = LocalClipboardManager.current - val scope = rememberCoroutineScope() var showSelectTextDialog by remember(note) { mutableStateOf(false) } var showDeleteAlertDialog by remember(note) { mutableStateOf(false) } var showBlockAlertDialog by remember(note) { mutableStateOf(false) } var showReportDialog by remember(note) { mutableStateOf(false) } - val backgroundColor = if (MaterialTheme.colors.isLight) { - MaterialTheme.colors.primary - } else { - MaterialTheme.colors.primary.copy(alpha = 0.32f).compositeOver(MaterialTheme.colors.background) - } - - val showToast = { stringResource: Int -> - scope.launch { - Toast.makeText( - context, - context.getString(stringResource), - Toast.LENGTH_SHORT - ).show() - } - } - if (popupExpanded) { + val context = LocalContext.current + val primaryLight = lightenColor(MaterialTheme.colors.primary, 0.1f) + val cardShape = RoundedCornerShape(5.dp) + val clipboardManager = LocalClipboardManager.current + val scope = rememberCoroutineScope() + + val backgroundColor = if (MaterialTheme.colors.isLight) { + MaterialTheme.colors.primary + } else { + MaterialTheme.colors.primary.copy(alpha = 0.32f).compositeOver(MaterialTheme.colors.background) + } + + val showToast = { stringResource: Int -> + scope.launch { + Toast.makeText( + context, + context.getString(stringResource), + Toast.LENGTH_SHORT + ).show() + } + } + val isOwnNote = accountViewModel.isLoggedUser(note.author) val isFollowingUser = !isOwnNote && accountViewModel.isFollowing(note.author) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt index 93e0e96e6..7997a232c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt @@ -29,36 +29,35 @@ fun ReplyInformation( accountViewModel: AccountViewModel, nav: (String) -> Unit ) { - var dupMentions by remember { mutableStateOf?>(null) } + var sortedMentions by remember { mutableStateOf?>(null) } LaunchedEffect(Unit) { launch(Dispatchers.IO) { - dupMentions = mentions.mapNotNull { LocalCache.checkGetOrCreateUser(it) } + sortedMentions = mentions.mapNotNull { LocalCache.checkGetOrCreateUser(it) } + ?.toSet()?.sortedBy { !accountViewModel.account.userProfile().isFollowingCached(it) } } } - if (dupMentions != null) { - ReplyInformation(replyTo, dupMentions, accountViewModel) { + if (sortedMentions != null) { + ReplyInformation(replyTo, sortedMentions) { nav("User/${it.pubkeyHex}") } } } @Composable -fun ReplyInformation( +private fun ReplyInformation( replyTo: List?, - dupMentions: List?, - accountViewModel: AccountViewModel, + sortedMentions: List?, prefix: String = "", onUserTagClick: (User) -> Unit ) { - val mentions = dupMentions?.toSet()?.sortedBy { !accountViewModel.account.userProfile().isFollowingCached(it) } - var expanded by remember { mutableStateOf((mentions?.size ?: 0) <= 2) } + var expanded by remember { mutableStateOf((sortedMentions?.size ?: 0) <= 2) } FlowRow() { - if (mentions != null && mentions.isNotEmpty()) { + if (sortedMentions != null && sortedMentions.isNotEmpty()) { if (replyTo != null && replyTo.isNotEmpty()) { - val repliesToDisplay = if (expanded) mentions else mentions.take(2) + val repliesToDisplay = if (expanded) sortedMentions else sortedMentions.take(2) Text( stringResource(R.string.replying_to), @@ -98,7 +97,7 @@ fun ReplyInformation( ) ClickableText( - AnnotatedString("${mentions.size - 2}"), + AnnotatedString("${sortedMentions.size - 2}"), style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary.copy(alpha = 0.52f), fontSize = 13.sp), onClick = { expanded = true } ) @@ -120,7 +119,7 @@ fun ReplyInformation( fun ReplyInformationChannel( replyTo: List?, mentions: List, - channel: Channel, + channelHex: String, accountViewModel: AccountViewModel, nav: (String) -> Unit ) { @@ -136,17 +135,19 @@ fun ReplyInformationChannel( } if (sortedMentions != null) { - ReplyInformationChannel( - replyTo, - sortedMentions, - channel, - onUserTagClick = { - nav("User/${it.pubkeyHex}") - }, - onChannelTagClick = { - nav("Channel/${it.idHex}") - } - ) + LoadChannel(channelHex) { channel -> + ReplyInformationChannel( + replyTo, + sortedMentions, + channel, + onUserTagClick = { + nav("User/${it.pubkeyHex}") + }, + onChannelTagClick = { + nav("Channel/${it.idHex}") + } + ) + } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt index 736976132..c95f01b6d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt @@ -92,6 +92,7 @@ private fun FeedLoaded( val accountState by accountViewModel.accountLiveData.observeAsState() val account = accountState?.account ?: return + val notificationCacheState = NotificationCache.live.observeAsState() val notificationCache = notificationCacheState.value ?: return @@ -99,9 +100,9 @@ private fun FeedLoaded( if (markAsRead.value) { for (note in state.feed.value) { note.event?.let { - val channel = note.channel() - val route = if (channel != null) { - "Channel/${channel.idHex}" + val channelHex = note.channelHex() + val route = if (channelHex != null) { + "Channel/$channelHex" } else { val replyAuthorBase = (note.event as? PrivateDmEvent) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt index 291ee3b4c..6f717972b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt @@ -33,6 +33,8 @@ import androidx.compose.ui.unit.dp import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import kotlin.time.ExperimentalTime +import kotlin.time.measureTimedValue @OptIn(ExperimentalMaterialApi::class) @Composable @@ -143,6 +145,7 @@ private fun WatchScrollToTop( } } +@OptIn(ExperimentalTime::class) @Composable private fun FeedLoaded( state: FeedState.Loaded, @@ -163,14 +166,17 @@ private fun FeedLoaded( state = listState ) { itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item -> - NoteCompose( - item, - routeForLastRead = routeForLastRead, - modifier = baseModifier, - isBoostedNote = false, - accountViewModel = accountViewModel, - nav = nav - ) + val (value, elapsed) = measureTimedValue { + NoteCompose( + item, + routeForLastRead = routeForLastRead, + modifier = baseModifier, + isBoostedNote = false, + accountViewModel = accountViewModel, + nav = nav + ) + } + println("AAA NoteCompose $elapsed ${item.event?.content()}") } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt index 4b99b3e9c..25bedd3d2 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt @@ -229,7 +229,7 @@ fun ChannelScreen( trailingIcon = { PostButton( onPost = { - val tagger = NewMessageTagger(channel, listOfNotNull(replyTo.value?.author), listOfNotNull(replyTo.value), channelScreenModel.message.text) + val tagger = NewMessageTagger(channel.idHex, listOfNotNull(replyTo.value?.author), listOfNotNull(replyTo.value), channelScreenModel.message.text) tagger.run() account.sendChannelMessage(tagger.message, channel.idHex, tagger.replyTos, tagger.mentions, wantsToMarkAsSensitive = false) channelScreenModel.message = TextFieldValue("") diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/LoadRedirectScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/LoadRedirectScreen.kt index 1cac914e9..8507d6f19 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/LoadRedirectScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/LoadRedirectScreen.kt @@ -21,7 +21,7 @@ import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.PrivateDmEvent @Composable -fun LoadRedirectScreen(eventId: String?, accountViewModel: AccountViewModel, navController: NavController) { +fun LoadRedirectScreen(eventId: String?, navController: NavController) { if (eventId == null) return val baseNote = LocalCache.checkGetOrCreateNote(eventId) ?: return @@ -31,7 +31,7 @@ fun LoadRedirectScreen(eventId: String?, accountViewModel: AccountViewModel, nav LaunchedEffect(key1 = noteState) { val event = note?.event - val channel = note?.channel() + val channelHex = note?.channelHex() if (event == null) { // stay here, loading @@ -41,9 +41,9 @@ fun LoadRedirectScreen(eventId: String?, accountViewModel: AccountViewModel, nav } else if (event is PrivateDmEvent) { navController.backQueue.removeLast() navController.navigate("Room/${note.author?.pubkeyHex}") - } else if (channel != null) { + } else if (channelHex != null) { navController.backQueue.removeLast() - navController.navigate("Channel/${channel.idHex}") + navController.navigate("Channel/$channelHex") } else { navController.backQueue.removeLast() navController.navigate("Note/${note.idHex}")