Showing private message notifications in the notification tab
This commit is contained in:
@@ -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<Boolean>(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import com.google.accompanist.flowlayout.FlowRow
|
|||||||
import com.vitorpamplona.amethyst.NotificationCache
|
import com.vitorpamplona.amethyst.NotificationCache
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.RoboHashCache
|
import com.vitorpamplona.amethyst.RoboHashCache
|
||||||
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
|
import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
|
||||||
@@ -70,6 +71,7 @@ fun NoteCompose(
|
|||||||
isQuotedNote: Boolean = false,
|
isQuotedNote: Boolean = false,
|
||||||
unPackReply: Boolean = true,
|
unPackReply: Boolean = true,
|
||||||
makeItShort: Boolean = false,
|
makeItShort: Boolean = false,
|
||||||
|
addMarginTop: Boolean = true,
|
||||||
parentBackgroundColor: Color? = null,
|
parentBackgroundColor: Color? = null,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
navController: NavController
|
navController: NavController
|
||||||
@@ -167,7 +169,7 @@ fun NoteCompose(
|
|||||||
.padding(
|
.padding(
|
||||||
start = if (!isBoostedNote) 12.dp else 0.dp,
|
start = if (!isBoostedNote) 12.dp else 0.dp,
|
||||||
end = 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) {
|
if (!isBoostedNote && !isQuotedNote) {
|
||||||
@@ -403,6 +405,34 @@ fun NoteCompose(
|
|||||||
|
|
||||||
ReactionsRow(note, accountViewModel)
|
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(
|
Divider(
|
||||||
modifier = Modifier.padding(top = 10.dp),
|
modifier = Modifier.padding(top = 10.dp),
|
||||||
thickness = 0.25.dp
|
thickness = 0.25.dp
|
||||||
|
|||||||
@@ -63,6 +63,14 @@ class BoostSetCard(val note: Note, val boostEvents: List<Note>) : Card() {
|
|||||||
override fun id() = note.idHex + "B" + createdAt
|
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 {
|
sealed class CardFeedState {
|
||||||
object Loading : CardFeedState()
|
object Loading : CardFeedState()
|
||||||
class Loaded(val feed: MutableState<List<Card>>) : CardFeedState()
|
class Loaded(val feed: MutableState<List<Card>>) : CardFeedState()
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
|||||||
import com.vitorpamplona.amethyst.ui.note.BadgeCompose
|
import com.vitorpamplona.amethyst.ui.note.BadgeCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.BoostSetCompose
|
import com.vitorpamplona.amethyst.ui.note.BoostSetCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.LikeSetCompose
|
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.MultiSetCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||||
import com.vitorpamplona.amethyst.ui.note.ZapSetCompose
|
import com.vitorpamplona.amethyst.ui.note.ZapSetCompose
|
||||||
@@ -134,6 +135,12 @@ private fun FeedLoaded(
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
routeForLastRead = routeForLastRead
|
routeForLastRead = routeForLastRead
|
||||||
)
|
)
|
||||||
|
is MessageSetCard -> MessageSetCompose(
|
||||||
|
messageSetCard = item,
|
||||||
|
routeForLastRead = routeForLastRead,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
navController = navController
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
|
|||||||
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
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.ReactionEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
import com.vitorpamplona.amethyst.service.model.RepostEvent
|
||||||
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
|
||||||
@@ -111,7 +112,9 @@ open class CardFeedViewModel(val dataSource: FeedFilter<Note>) : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val textNoteCards = notes.filter { it.event !is ReactionEvent && it.event !is RepostEvent && it.event !is LnZapEvent }.map {
|
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)
|
BadgeCard(it)
|
||||||
} else {
|
} else {
|
||||||
NoteCard(it)
|
NoteCard(it)
|
||||||
|
|||||||
@@ -219,6 +219,6 @@
|
|||||||
<string name="mastodon_proof_url_template" translatable="false">https://<server>/<user>/<proof post></string>
|
<string name="mastodon_proof_url_template" translatable="false">https://<server>/<user>/<proof post></string>
|
||||||
<string name="twitter_proof_url_template" translatable="false">https://twitter.com/<user>/status/<proof post></string>
|
<string name="twitter_proof_url_template" translatable="false">https://twitter.com/<user>/status/<proof post></string>
|
||||||
|
|
||||||
|
<string name="private_conversation_notification">"<Unable to decrypt private message>\n\nYou were cited in a private/encrypted conversation between %1$s and %2$s."</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user