diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt new file mode 100644 index 000000000..5d7e07ab7 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt @@ -0,0 +1,121 @@ +package com.vitorpamplona.amethyst.ui.note + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +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.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.compositeOver +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.vitorpamplona.amethyst.NotificationCache +import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent +import com.vitorpamplona.amethyst.ui.screen.MessageSetCard +import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun MessageSetCompose(messageSetCard: MessageSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) { + val noteState by messageSetCard.note.live().metadata.observeAsState() + val note = noteState?.note + + val accountState by accountViewModel.accountLiveData.observeAsState() + val account = accountState?.account ?: return + + val context = LocalContext.current.applicationContext + + val noteEvent = note?.event + var popupExpanded by remember { mutableStateOf(false) } + + if (note == null) { + BlankNote(Modifier, isInnerNote) + } else { + var isNew by remember { mutableStateOf(false) } + + LaunchedEffect(key1 = messageSetCard) { + isNew = messageSetCard.createdAt() > NotificationCache.load(routeForLastRead, context) + + NotificationCache.markAsRead(routeForLastRead, messageSetCard.createdAt(), context) + } + + var backgroundColor = if (isNew) { + MaterialTheme.colors.primary.copy(0.12f).compositeOver(MaterialTheme.colors.background) + } else { + MaterialTheme.colors.background + } + + Column( + modifier = Modifier.background(backgroundColor).combinedClickable( + onClick = { + if (noteEvent !is ChannelMessageEvent) { + navController.navigate("Note/${note.idHex}") { + launchSingleTop = true + } + } else { + note.channel()?.let { + navController.navigate("Channel/${it.idHex}") + } + } + }, + onLongClick = { popupExpanded = true } + ) + ) { + Row( + modifier = Modifier + .padding( + start = if (!isInnerNote) 12.dp else 0.dp, + end = if (!isInnerNote) 12.dp else 0.dp, + top = 10.dp + ) + ) { + // Draws the like picture outside the boosted card. + if (!isInnerNote) { + Box( + modifier = Modifier + .width(55.dp) + .padding(top = 5.dp) + ) { + Icon( + painter = painterResource(R.drawable.ic_dm), + null, + modifier = Modifier.size(16.dp).align(Alignment.TopEnd), + tint = MaterialTheme.colors.primary + ) + } + } + + Column(modifier = Modifier.padding(start = if (!isInnerNote) 10.dp else 0.dp)) { + NoteCompose( + baseNote = note, + routeForLastRead = null, + isBoostedNote = true, + addMarginTop = false, + parentBackgroundColor = backgroundColor, + accountViewModel = accountViewModel, + navController = navController + ) + + NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel) + } + } + } + } +} 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 a41b277a2..44a958db5 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 @@ -39,6 +39,7 @@ import com.google.accompanist.flowlayout.FlowRow import com.vitorpamplona.amethyst.NotificationCache import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.RoboHashCache +import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent @@ -70,6 +71,7 @@ fun NoteCompose( isQuotedNote: Boolean = false, unPackReply: Boolean = true, makeItShort: Boolean = false, + addMarginTop: Boolean = true, parentBackgroundColor: Color? = null, accountViewModel: AccountViewModel, navController: NavController @@ -167,7 +169,7 @@ fun NoteCompose( .padding( start = if (!isBoostedNote) 12.dp else 0.dp, end = if (!isBoostedNote) 12.dp else 0.dp, - top = 10.dp + top = if (addMarginTop) 10.dp else 0.dp ) ) { if (!isBoostedNote && !isQuotedNote) { @@ -403,6 +405,34 @@ fun NoteCompose( ReactionsRow(note, accountViewModel) + Divider( + modifier = Modifier.padding(top = 10.dp), + thickness = 0.25.dp + ) + } else if (noteEvent is PrivateDmEvent && + noteEvent.recipientPubKey() != account.userProfile().pubkeyHex && + note.author != account.userProfile() + ) { + val recepient = noteEvent.recipientPubKey()?.let { LocalCache.checkGetOrCreateUser(it) } + + TranslateableRichTextViewer( + stringResource( + id = R.string.private_conversation_notification, + "@${note.author?.pubkeyNpub()}", + "@${recepient?.pubkeyNpub()}" + ), + canPreview = !makeItShort, + Modifier.fillMaxWidth(), + noteEvent.tags(), + backgroundColor, + accountViewModel, + navController + ) + + if (!makeItShort) { + ReactionsRow(note, accountViewModel) + } + Divider( modifier = Modifier.padding(top = 10.dp), thickness = 0.25.dp diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt index 950430cc1..bdacb2639 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt @@ -63,6 +63,14 @@ class BoostSetCard(val note: Note, val boostEvents: List) : Card() { override fun id() = note.idHex + "B" + createdAt } +class MessageSetCard(val note: Note) : Card() { + override fun createdAt(): Long { + return note.createdAt() ?: 0 + } + + override fun id() = note.idHex +} + sealed class CardFeedState { object Loading : CardFeedState() class Loaded(val feed: MutableState>) : CardFeedState() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt index 7bd61ec1d..df4fa0ebf 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt @@ -21,6 +21,7 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import com.vitorpamplona.amethyst.ui.note.BadgeCompose import com.vitorpamplona.amethyst.ui.note.BoostSetCompose import com.vitorpamplona.amethyst.ui.note.LikeSetCompose +import com.vitorpamplona.amethyst.ui.note.MessageSetCompose import com.vitorpamplona.amethyst.ui.note.MultiSetCompose import com.vitorpamplona.amethyst.ui.note.NoteCompose import com.vitorpamplona.amethyst.ui.note.ZapSetCompose @@ -134,6 +135,12 @@ private fun FeedLoaded( navController = navController, routeForLastRead = routeForLastRead ) + is MessageSetCard -> MessageSetCompose( + messageSetCard = item, + routeForLastRead = routeForLastRead, + accountViewModel = accountViewModel, + navController = navController + ) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt index b7f9b1770..3060f307a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt @@ -9,6 +9,7 @@ import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent import com.vitorpamplona.amethyst.service.model.LnZapEvent +import com.vitorpamplona.amethyst.service.model.PrivateDmEvent import com.vitorpamplona.amethyst.service.model.ReactionEvent import com.vitorpamplona.amethyst.service.model.RepostEvent import com.vitorpamplona.amethyst.ui.dal.FeedFilter @@ -111,7 +112,9 @@ open class CardFeedViewModel(val dataSource: FeedFilter) : ViewModel() { } val textNoteCards = notes.filter { it.event !is ReactionEvent && it.event !is RepostEvent && it.event !is LnZapEvent }.map { - if (it.event is BadgeAwardEvent) { + if (it.event is PrivateDmEvent) { + MessageSetCard(it) + } else if (it.event is BadgeAwardEvent) { BadgeCard(it) } else { NoteCard(it) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f3c2a9d95..f6d729ed9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -219,6 +219,6 @@ https://<server>/<user>/<proof post> https://twitter.com/<user>/status/<proof post> - + "<Unable to decrypt private message>\n\nYou were cited in a private/encrypted conversation between %1$s and %2$s."