- Adds Immutable Lists to avoid recompositions

- Moves NavController to a lambda to avoid recompositions
- Moves AccountViewModel builder to a ViewModel Factory to avoid recompositions
- Reduces notifications invalidation requests when starting the screen.
This commit is contained in:
Vitor Pamplona
2023-05-24 12:34:09 -04:00
parent 4841d4004e
commit f1ecaf1ee5
66 changed files with 847 additions and 768 deletions
+3 -1
View File
@@ -82,7 +82,6 @@ android {
lint {
disable 'MissingTranslation'
}
}
dependencies {
@@ -183,6 +182,9 @@ dependencies {
implementation "com.patrykandpatrick.vico:views:${vico_version}"
implementation "com.patrykandpatrick.vico:compose-m2:${vico_version}"
// immutable collections to avoid recomposition
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5")
// Automatic memory leak detection
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.11'
@@ -3,7 +3,6 @@ package com.vitorpamplona.amethyst.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
@@ -14,7 +13,7 @@ fun TranslatableRichTextViewer(
tags: List<List<String>>?,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) = ExpandableRichTextViewer(
content,
canPreview,
@@ -22,5 +21,5 @@ fun TranslatableRichTextViewer(
tags,
backgroundColor,
accountViewModel,
navController
nav
)
@@ -8,6 +8,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
object NotificationCache {
// TODO: This must be account-based
val lastReadByRoute = mutableMapOf<String, Long>()
fun markAsRead(route: String, timestampInSecs: Long) {
@@ -2,6 +2,8 @@ package com.vitorpamplona.amethyst.model
import android.content.res.Resources
import android.util.Log
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.core.os.ConfigurationCompat
import androidx.lifecycle.LiveData
import com.vitorpamplona.amethyst.service.FileHeader
@@ -43,6 +45,7 @@ val GLOBAL_FOLLOWS = " Global "
val KIND3_FOLLOWS = " All Follows "
@OptIn(DelicateCoroutinesApi::class)
@Stable
class Account(
val loggedIn: Persona,
var followingChannels: Set<String> = DefaultChannels,
@@ -1166,4 +1169,5 @@ class AccountLiveData(private val account: Account) : LiveData<AccountState>(Acc
}
}
@Immutable
class AccountState(val account: Account)
@@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.model
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.lifecycle.LiveData
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
@@ -442,4 +443,5 @@ class NoteLiveData(val note: Note) : LiveData<NoteState>(NoteState(note)) {
}
}
@Immutable
class NoteState(val note: Note)
@@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.model
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.lifecycle.LiveData
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
@@ -420,4 +421,5 @@ class UserLiveData(val user: User) : LiveData<UserState>(UserState(user)) {
}
}
@Immutable
class UserState(val user: User)
@@ -30,6 +30,7 @@ class Relay(
) {
val seconds = if (proxy != null) 20L else 10L
val duration = Duration.ofSeconds(seconds)
private val httpClient = OkHttpClient.Builder()
.proxy(proxy)
.readTimeout(duration)
@@ -51,7 +51,6 @@ import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
@@ -73,7 +72,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Composable
fun JoinUserOrChannelView(onClose: () -> Unit, account: Account, navController: NavController) {
fun JoinUserOrChannelView(onClose: () -> Unit, account: Account, nav: (String) -> Unit) {
val searchBarViewModel: SearchBarViewModel = viewModel()
searchBarViewModel.account = account
@@ -116,7 +115,7 @@ fun JoinUserOrChannelView(onClose: () -> Unit, account: Account, navController:
Spacer(modifier = Modifier.height(15.dp))
RenderSeach(searchBarViewModel, account, navController)
RenderSeach(searchBarViewModel, account, nav)
}
}
}
@@ -127,7 +126,7 @@ fun JoinUserOrChannelView(onClose: () -> Unit, account: Account, navController:
private fun RenderSeach(
searchBarViewModel: SearchBarViewModel,
account: Account,
navController: NavController
nav: (String) -> Unit
) {
val scope = rememberCoroutineScope()
val listState = rememberLazyListState()
@@ -275,7 +274,7 @@ private fun RenderSeach(
UserComposeForChat(
item,
account = account,
navController = navController
nav = nav
)
}
@@ -295,7 +294,7 @@ private fun RenderSeach(
channelLastTime = null,
channelLastContent = item.info.about,
false,
onClick = { navController.navigate("Channel/${item.idHex}") }
onClick = { nav("Channel/${item.idHex}") }
)
}
}
@@ -307,12 +306,12 @@ private fun RenderSeach(
fun UserComposeForChat(
baseUser: User,
account: Account,
navController: NavController
nav: (String) -> Unit
) {
Column(
modifier =
Modifier.clickable(
onClick = { navController.navigate("Room/${baseUser.pubkeyHex}") }
onClick = { nav("Room/${baseUser.pubkeyHex}") }
)
) {
Row(
@@ -324,7 +323,7 @@ fun UserComposeForChat(
),
verticalAlignment = Alignment.CenterVertically
) {
UserPicture(baseUser, navController, account.userProfile(), 55.dp)
UserPicture(baseUser, nav, account.userProfile(), 55.dp)
Column(modifier = Modifier.padding(start = 10.dp).weight(1f)) {
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -28,7 +28,6 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
@@ -39,7 +38,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, accountViewModel: AccountViewModel, navController: NavController) {
fun NewMediaView(uri: Uri, onClose: () -> Unit, postViewModel: NewMediaModel, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val account = accountViewModel.accountLiveData.value?.account ?: return
val resolver = LocalContext.current.contentResolver
val context = LocalContext.current
@@ -57,7 +57,6 @@ import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
@@ -75,7 +74,7 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, account: Account, accountViewModel: AccountViewModel, navController: NavController) {
fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, account: Account, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val postViewModel: NewPostViewModel = viewModel()
val context = LocalContext.current
@@ -292,7 +291,7 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = n
true,
MaterialTheme.colors.background,
accountViewModel,
navController
nav
)
} else if (noProtocolUrlValidator.matcher(myUrlPreview).matches()) {
UrlPreview("https://$myUrlPreview", myUrlPreview)
@@ -15,14 +15,13 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.actions.JoinUserOrChannelView
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
@Composable
fun ChannelFabColumn(account: Account, navController: NavController) {
fun ChannelFabColumn(account: Account, nav: (String) -> Unit) {
var isOpen by remember {
mutableStateOf(false)
}
@@ -40,7 +39,7 @@ fun ChannelFabColumn(account: Account, navController: NavController) {
}
if (wantsToJoinChannelOrUser) {
JoinUserOrChannelView({ wantsToJoinChannelOrUser = false }, account = account, navController = navController)
JoinUserOrChannelView({ wantsToJoinChannelOrUser = false }, account = account, nav = nav)
}
Column() {
@@ -16,20 +16,19 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.ui.actions.NewPostView
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
fun NewNoteButton(account: Account, accountViewModel: AccountViewModel, navController: NavController) {
fun NewNoteButton(account: Account, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
var wantsToPost by remember {
mutableStateOf(false)
}
if (wantsToPost) {
NewPostView({ wantsToPost = false }, account = account, accountViewModel = accountViewModel, navController = navController)
NewPostView({ wantsToPost = false }, account = account, accountViewModel = accountViewModel, nav = nav)
}
OutlinedButton(
@@ -22,9 +22,11 @@ class BundledUpdate(
private var onlyOneInBlock = AtomicBoolean()
private var invalidatesAgain = false
fun invalidate() {
fun invalidate(ignoreIfDoing: Boolean = false) {
if (onlyOneInBlock.getAndSet(true)) {
invalidatesAgain = true
if (!ignoreIfDoing) {
invalidatesAgain = true
}
return
}
@@ -5,18 +5,17 @@ import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.text.AnnotatedString
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.note.toShortenHex
@Composable
fun ClickableNoteTag(
baseNote: Note,
navController: NavController
nav: (String) -> Unit
) {
ClickableText(
text = AnnotatedString("@${baseNote.idNote().toShortenHex()}"),
onClick = { navController.navigate("Note/${baseNote.idHex}") },
onClick = { nav("Note/${baseNote.idHex}") },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
)
}
@@ -37,7 +37,6 @@ import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -52,16 +51,16 @@ import kotlinx.coroutines.withContext
@Composable
fun ClickableRoute(
nip19: Nip19.Return,
navController: NavController
nav: (String) -> Unit
) {
if (nip19.type == Nip19.Type.USER) {
DisplayUser(nip19, navController)
DisplayUser(nip19, nav)
} else if (nip19.type == Nip19.Type.ADDRESS) {
DisplayAddress(nip19, navController)
DisplayAddress(nip19, nav)
} else if (nip19.type == Nip19.Type.NOTE) {
DisplayNote(nip19, navController)
DisplayNote(nip19, nav)
} else if (nip19.type == Nip19.Type.EVENT) {
DisplayEvent(nip19, navController)
DisplayEvent(nip19, nav)
} else {
Text(
"@${nip19.hex}${nip19.additionalChars} "
@@ -72,7 +71,7 @@ fun ClickableRoute(
@Composable
private fun DisplayEvent(
nip19: Nip19.Return,
navController: NavController
nav: (String) -> Unit
) {
var noteBase by remember { mutableStateOf<Note?>(null) }
@@ -92,28 +91,28 @@ private fun DisplayEvent(
clickablePart = "@${note.idDisplayNote()}",
suffix = "${nip19.additionalChars} ",
route = "Channel/${nip19.hex}",
navController = navController
nav = nav
)
} else if (note.event is PrivateDmEvent) {
CreateClickableText(
clickablePart = "@${note.idDisplayNote()}",
suffix = "${nip19.additionalChars} ",
route = "Room/${note.author?.pubkeyHex}",
navController = navController
nav = nav
)
} else if (channel != null) {
CreateClickableText(
clickablePart = channel.toBestDisplayName(),
suffix = "${nip19.additionalChars} ",
route = "Channel/${channel.idHex}",
navController = navController
nav = nav
)
} else {
CreateClickableText(
clickablePart = "@${note.idDisplayNote()}",
suffix = "${nip19.additionalChars} ",
route = "Event/${nip19.hex}",
navController = navController
nav = nav
)
}
}
@@ -128,7 +127,7 @@ private fun DisplayEvent(
@Composable
private fun DisplayNote(
nip19: Nip19.Return,
navController: NavController
nav: (String) -> Unit
) {
var noteBase by remember { mutableStateOf<Note?>(null) }
@@ -148,28 +147,28 @@ private fun DisplayNote(
clickablePart = "@${note.idDisplayNote()}",
suffix = "${nip19.additionalChars} ",
route = "Channel/${nip19.hex}",
navController = navController
nav = nav
)
} else if (note.event is PrivateDmEvent) {
CreateClickableText(
clickablePart = "@${note.idDisplayNote()}",
suffix = "${nip19.additionalChars} ",
route = "Room/${note.author?.pubkeyHex}",
navController = navController
nav = nav
)
} else if (channel != null) {
CreateClickableText(
clickablePart = channel.toBestDisplayName(),
suffix = "${nip19.additionalChars} ",
route = "Channel/${note.channel()?.idHex}",
navController = navController
nav = nav
)
} else {
CreateClickableText(
clickablePart = "@${note.idDisplayNote()}",
suffix = "${nip19.additionalChars} ",
route = "Note/${nip19.hex}",
navController = navController
nav = nav
)
}
}
@@ -184,7 +183,7 @@ private fun DisplayNote(
@Composable
private fun DisplayAddress(
nip19: Nip19.Return,
navController: NavController
nav: (String) -> Unit
) {
var noteBase by remember { mutableStateOf<Note?>(null) }
@@ -202,7 +201,7 @@ private fun DisplayAddress(
clickablePart = "@${note.idDisplayNote()}",
suffix = "${nip19.additionalChars} ",
route = "Note/${nip19.hex}",
navController = navController
nav = nav
)
}
@@ -216,7 +215,7 @@ private fun DisplayAddress(
@Composable
private fun DisplayUser(
nip19: Nip19.Return,
navController: NavController
nav: (String) -> Unit
) {
var userBase by remember { mutableStateOf<User?>(null) }
@@ -238,7 +237,7 @@ private fun DisplayUser(
suffix = "${nip19.additionalChars} ",
tags = userTags,
route = route,
navController = navController
nav = nav
)
}
}
@@ -257,7 +256,7 @@ fun CreateClickableText(
overrideColor: Color? = null,
fontWeight: FontWeight = FontWeight.Normal,
route: String,
navController: NavController
nav: (String) -> Unit
) {
ClickableText(
text = buildAnnotatedString {
@@ -272,7 +271,7 @@ fun CreateClickableText(
append(suffix)
}
},
onClick = { navController.navigate(route) }
onClick = { nav(route) }
)
}
@@ -378,21 +377,21 @@ fun CreateClickableTextWithEmoji(
overrideColor: Color? = null,
fontWeight: FontWeight = FontWeight.Normal,
route: String,
navController: NavController
nav: (String) -> Unit
) {
val emojis = remember(tags) {
tags?.filter { it.size > 2 && it[0] == "emoji" }?.associate { ":${it[1]}:" to it[2] } ?: emptyMap()
}
if (emojis.isEmpty()) {
CreateClickableText(clickablePart, suffix, overrideColor, fontWeight, route, navController)
CreateClickableText(clickablePart, suffix, overrideColor, fontWeight, route, nav)
} else {
val myList = remember {
assembleAnnotatedList(clickablePart, emojis)
}
ClickableInLineIconRenderer(myList, LocalTextStyle.current.copy(color = overrideColor ?: MaterialTheme.colors.primary, fontWeight = fontWeight).toSpanStyle()) {
navController.navigate(route)
nav(route)
}
val myList2 = remember {
@@ -7,18 +7,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.text.AnnotatedString
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.model.User
@Composable
fun ClickableUserTag(
user: User,
navController: NavController
nav: (String) -> Unit
) {
val innerUserState by user.live().metadata.observeAsState()
ClickableText(
text = AnnotatedString("@${innerUserState?.user?.toBestDisplayName()}"),
onClick = { navController.navigate("User/${innerUserState?.user?.pubkeyHex}") },
onClick = { nav("User/${innerUserState?.user?.pubkeyHex}") },
style = LocalTextStyle.current.copy(color = MaterialTheme.colors.primary)
)
}
@@ -25,7 +25,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -39,7 +38,7 @@ fun ExpandableRichTextViewer(
tags: List<List<String>>?,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
var showFullText by remember { mutableStateOf(false) }
@@ -69,7 +68,7 @@ fun ExpandableRichTextViewer(
tags,
backgroundColor,
accountViewModel,
navController
nav
)
if (content.length > whereToCut && !showFullText) {
@@ -26,7 +26,6 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.halilibo.richtext.markdown.Markdown
import com.halilibo.richtext.markdown.MarkdownParseOptions
@@ -94,19 +93,20 @@ fun RichTextViewer(
tags: List<List<String>>?,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val isMarkdown = remember { isMarkdown(content) }
Column(modifier = modifier) {
if (isMarkdown) {
RenderContentAsMarkdown(content, backgroundColor, tags, navController)
RenderContentAsMarkdown(content, backgroundColor, tags, nav)
} else {
RenderRegular(content, tags, canPreview, backgroundColor, accountViewModel, navController)
RenderRegular(content, tags, canPreview, backgroundColor, accountViewModel, nav)
}
}
}
@Stable
class RichTextViewerState(
val content: String,
val urlSet: Set<String>,
@@ -122,10 +122,10 @@ private fun RenderRegular(
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
var processedState by remember {
mutableStateOf<RichTextViewerState?>(RichTextViewerState(content, emptySet(), emptyMap(), emptyList(), emptyMap()))
var state by remember {
mutableStateOf(RichTextViewerState(content, emptySet(), emptyMap(), emptyList(), emptyMap()))
}
val scope = rememberCoroutineScope()
@@ -149,80 +149,73 @@ private fun RenderRegular(
val emojiMap = tags?.filter { it.size > 2 && it[0] == "emoji" }?.associate { ":${it[1]}:" to it[2] } ?: emptyMap()
if (urlSet.isNotEmpty() || emojiMap.isNotEmpty()) {
processedState = RichTextViewerState(content, urlSet, imagesForPager, imageList, emojiMap)
state = RichTextViewerState(content, urlSet, imagesForPager, imageList, emojiMap)
}
}
}
// FlowRow doesn't work well with paragraphs. So we need to split them
processedState?.let { state ->
content.split('\n').forEach { paragraph ->
FlowRow() {
val s = if (isArabic(paragraph)) {
paragraph.trim().split(' ')
.reversed()
} else {
paragraph.trim().split(' ')
}
s.forEach { word: String ->
if (canPreview) {
// Explicit URL
val img = state.imagesForPager[word]
if (img != null) {
ZoomableContentView(img, state.imageList)
} else if (state.urlSet.contains(word)) {
UrlPreview(word, "$word ")
} else if (state.customEmoji.any { word.contains(it.key) }) {
RenderCustomEmoji(word, state.customEmoji)
} else if (word.startsWith("lnbc", true)) {
MayBeInvoicePreview(word)
} else if (word.startsWith("lnurl", true)) {
MayBeWithdrawal(word)
} else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) {
ClickableEmail(word)
} else if (word.length > 6 && Patterns.PHONE.matcher(word).matches()) {
ClickablePhone(word)
} else if (isBechLink(word)) {
BechLink(
content.split('\n').forEach { paragraph ->
FlowRow() {
val s = if (isArabic(paragraph)) {
paragraph.trim().split(' ')
.reversed()
} else {
paragraph.trim().split(' ')
}
s.forEach { word: String ->
if (canPreview) {
// Explicit URL
val img = state.imagesForPager[word]
if (img != null) {
ZoomableContentView(img, state.imageList)
} else if (state.urlSet.contains(word)) {
UrlPreview(word, "$word ")
} else if (state.customEmoji.any { word.contains(it.key) }) {
RenderCustomEmoji(word, state.customEmoji)
} else if (word.startsWith("lnbc", true)) {
MayBeInvoicePreview(word)
} else if (word.startsWith("lnurl", true)) {
MayBeWithdrawal(word)
} else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) {
ClickableEmail(word)
} else if (word.length > 6 && Patterns.PHONE.matcher(word).matches()) {
ClickablePhone(word)
} else if (isBechLink(word)) {
BechLink(
word,
canPreview,
backgroundColor,
accountViewModel,
nav
)
} else if (word.startsWith("#")) {
if (tagIndex.matcher(word).matches() && tags != null) {
TagLink(
word,
tags,
canPreview,
backgroundColor,
accountViewModel,
navController
nav
)
} else if (word.startsWith("#")) {
if (tagIndex.matcher(word).matches() && tags != null) {
TagLink(
word,
tags,
canPreview,
backgroundColor,
accountViewModel,
navController
)
} else if (hashTagsPattern.matcher(word).matches()) {
HashTag(word, navController)
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
} else if (noProtocolUrlValidator.matcher(word).matches()) {
val matcher = noProtocolUrlValidator.matcher(word)
matcher.find()
val url = matcher.group(1) // url
val additionalChars = matcher.group(4) ?: "" // additional chars
} else if (hashTagsPattern.matcher(word).matches()) {
HashTag(word, nav)
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
} else if (noProtocolUrlValidator.matcher(word).matches()) {
val matcher = noProtocolUrlValidator.matcher(word)
matcher.find()
val url = matcher.group(1) // url
val additionalChars = matcher.group(4) ?: "" // additional chars
if (url != null) {
ClickableUrl(url, "https://$url")
Text("$additionalChars ")
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
if (url != null) {
ClickableUrl(url, "https://$url")
Text("$additionalChars ")
} else {
Text(
text = "$word ",
@@ -230,71 +223,76 @@ private fun RenderRegular(
)
}
} else {
if (state.urlSet.contains(word)) {
ClickableUrl("$word ", word)
} else if (word.startsWith("lnurl", true)) {
val lnWithdrawal = LnWithdrawalUtil.findWithdrawal(word)
if (lnWithdrawal != null) {
ClickableWithdrawal(withdrawalString = lnWithdrawal)
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
} else if (state.customEmoji.any { word.contains(it.key) }) {
RenderCustomEmoji(word, state.customEmoji)
} else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) {
ClickableEmail(word)
} else if (Patterns.PHONE.matcher(word).matches() && word.length > 6) {
ClickablePhone(word)
} else if (isBechLink(word)) {
BechLink(
word,
canPreview,
backgroundColor,
accountViewModel,
navController
)
} else if (word.startsWith("#")) {
if (tagIndex.matcher(word).matches() && tags != null) {
TagLink(
word,
tags,
canPreview,
backgroundColor,
accountViewModel,
navController
)
} else if (hashTagsPattern.matcher(word).matches()) {
HashTag(word, navController)
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
} else if (noProtocolUrlValidator.matcher(word).matches()) {
val matcher = noProtocolUrlValidator.matcher(word)
matcher.find()
val url = matcher.group(1) // url
val additionalChars = matcher.group(4) ?: "" // additional chars
if (url != null) {
ClickableUrl(url, "https://$url")
Text("$additionalChars ")
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
} else {
if (state.urlSet.contains(word)) {
ClickableUrl("$word ", word)
} else if (word.startsWith("lnurl", true)) {
val lnWithdrawal = LnWithdrawalUtil.findWithdrawal(word)
if (lnWithdrawal != null) {
ClickableWithdrawal(withdrawalString = lnWithdrawal)
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
} else if (state.customEmoji.any { word.contains(it.key) }) {
RenderCustomEmoji(word, state.customEmoji)
} else if (Patterns.EMAIL_ADDRESS.matcher(word).matches()) {
ClickableEmail(word)
} else if (Patterns.PHONE.matcher(word).matches() && word.length > 6) {
ClickablePhone(word)
} else if (isBechLink(word)) {
BechLink(
word,
canPreview,
backgroundColor,
accountViewModel,
nav
)
} else if (word.startsWith("#")) {
if (tagIndex.matcher(word).matches() && tags != null) {
TagLink(
word,
tags,
canPreview,
backgroundColor,
accountViewModel,
nav
)
} else if (hashTagsPattern.matcher(word).matches()) {
HashTag(word, nav)
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
} else if (noProtocolUrlValidator.matcher(word).matches()) {
val matcher = noProtocolUrlValidator.matcher(word)
matcher.find()
val url = matcher.group(1) // url
val additionalChars = matcher.group(4) ?: "" // additional chars
if (url != null) {
ClickableUrl(url, "https://$url")
Text("$additionalChars ")
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
} else {
Text(
text = "$word ",
style = LocalTextStyle.current.copy(textDirection = TextDirection.Content)
)
}
}
}
@@ -311,7 +309,7 @@ fun RenderCustomEmoji(word: String, customEmoji: Map<String, String>) {
}
@Composable
private fun RenderContentAsMarkdown(content: String, backgroundColor: Color, tags: List<List<String>>?, navController: NavController) {
private fun RenderContentAsMarkdown(content: String, backgroundColor: Color, tags: List<List<String>>?, nav: (String) -> Unit) {
val myMarkDownStyle = richTextDefaults.copy(
codeBlockStyle = richTextDefaults.codeBlockStyle?.copy(
textStyle = TextStyle(
@@ -416,7 +414,7 @@ private fun RenderContentAsMarkdown(content: String, backgroundColor: Color, tag
) { link ->
val route = uriToRoute(link)
if (route != null) {
navController.navigate(route)
nav(route)
} else {
runCatching { uri.openUri(link) }
}
@@ -572,12 +570,14 @@ fun isBechLink(word: String): Boolean {
}
@Composable
fun BechLink(word: String, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, navController: NavController) {
fun BechLink(word: String, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
var nip19Route by remember { mutableStateOf<Nip19.Return?>(null) }
var baseNotePair by remember { mutableStateOf<Pair<Note, String?>?>(null) }
val scope = rememberCoroutineScope()
LaunchedEffect(key1 = word) {
withContext(Dispatchers.IO) {
scope.launch(Dispatchers.IO) {
Nip19.uriToRoute(word)?.let {
if (it.type == Nip19.Type.NOTE || it.type == Nip19.Type.EVENT || it.type == Nip19.Type.ADDRESS) {
LocalCache.checkGetOrCreateNote(it.hex)?.let { note ->
@@ -606,7 +606,7 @@ fun BechLink(word: String, canPreview: Boolean, backgroundColor: Color, accountV
),
parentBackgroundColor = backgroundColor,
isQuotedNote = true,
navController = navController
nav = nav
)
if (!it.second.isNullOrEmpty()) {
Text(
@@ -614,21 +614,23 @@ fun BechLink(word: String, canPreview: Boolean, backgroundColor: Color, accountV
)
}
} ?: nip19Route?.let {
ClickableRoute(it, navController)
ClickableRoute(it, nav)
} ?: Text(text = "$word ")
} else {
nip19Route?.let {
ClickableRoute(it, navController)
ClickableRoute(it, nav)
} ?: Text(text = "$word ")
}
}
@Composable
fun HashTag(word: String, navController: NavController) {
fun HashTag(word: String, nav: (String) -> Unit) {
var tagSuffixPair by remember { mutableStateOf<Pair<String, String?>?>(null) }
val scope = rememberCoroutineScope()
LaunchedEffect(key1 = word) {
withContext(Dispatchers.IO) {
scope.launch(Dispatchers.IO) {
val hashtagMatcher = hashTagsPattern.matcher(word)
val (myTag, mySuffix) = try {
@@ -646,7 +648,7 @@ fun HashTag(word: String, navController: NavController) {
}
tagSuffixPair?.let { tagPair ->
val hashtagIcon = checkForHashtagWithIcon(tagPair.first)
val hashtagIcon = remember(tagPair.first) { checkForHashtagWithIcon(tagPair.first) }
ClickableText(
text = buildAnnotatedString {
withStyle(
@@ -655,7 +657,7 @@ fun HashTag(word: String, navController: NavController) {
append("#${tagPair.first}")
}
},
onClick = { navController.navigate("Hashtag/${tagPair.first}") }
onClick = { nav("Hashtag/${tagPair.first}") }
)
if (hashtagIcon != null) {
@@ -701,7 +703,7 @@ fun HashTag(word: String, navController: NavController) {
}
@Composable
fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, navController: NavController) {
fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
var baseUserPair by remember { mutableStateOf<Pair<User, String?>?>(null) }
var baseNotePair by remember { mutableStateOf<Pair<Note, String?>?>(null) }
@@ -753,7 +755,7 @@ fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgro
suffix = "${it.second} ",
tags = userTags,
route = route,
navController = navController
nav = nav
)
}
@@ -773,13 +775,13 @@ fun TagLink(word: String, tags: List<List<String>>, canPreview: Boolean, backgro
),
parentBackgroundColor = backgroundColor,
isQuotedNote = true,
navController = navController
nav = nav
)
it.second?.ifBlank { null }?.let {
Text(text = "$it ")
}
} else {
ClickableNoteTag(it.first, navController)
ClickableNoteTag(it.first, nav)
Text(text = "${it.second} ")
}
}
@@ -132,6 +132,7 @@ fun RobohashAsyncImageProxy(
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality
) {
val proxy = remember(model) { model.proxyUrl() }
if (proxy == null) {
RobohashAsyncImage(
robot = robot,
@@ -37,7 +37,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import com.vitorpamplona.amethyst.NotificationCache
@@ -116,24 +115,26 @@ private fun RowScope.HasNewItemsIcon(
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = remember(accountState) { accountState?.account } ?: return
val notifState = NotificationCache.live.observeAsState()
val notif = remember(notifState) { notifState.value } ?: return
val notifState by NotificationCache.live.observeAsState()
val notif = remember(notifState) { notifState?.cache } ?: return
var hasNewItems by remember { mutableStateOf<Boolean>(false) }
LaunchedEffect(key1 = notif) {
LaunchedEffect(key1 = notifState, key2 = accountState) {
scope.launch(Dispatchers.IO) {
val newHasNewItems = route.hasNewItems(account, notif.cache, emptySet())
val newHasNewItems = route.hasNewItems(account, notif, emptySet())
println("Notification Change ${route.route} $hasNewItems -> $newHasNewItems")
if (newHasNewItems != hasNewItems) {
hasNewItems = newHasNewItems
}
}
}
LaunchedEffect(Unit) {
LaunchedEffect(accountState) {
scope.launch(Dispatchers.IO) {
LocalCache.live.newEventBundles.collect {
val newHasNewItems = route.hasNewItems(account, notif.cache, it)
val newHasNewItems = route.hasNewItems(account, notif, it)
println("Notification From Base ${route.route} $hasNewItems -> $newHasNewItems")
if (newHasNewItems != hasNewItems) {
hasNewItems = newHasNewItems
}
@@ -182,7 +183,7 @@ private fun RowScope.BottomIcon(
iconSize: Dp,
base: String,
hasNewItems: Boolean,
navController: NavController,
navController: NavHostController,
onClick: (Boolean) -> Unit
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
@@ -82,6 +82,14 @@ fun AppNavigation(
}
}
val nav = remember {
{ route: String ->
if (getRouteWithArguments(navController) != route) {
navController.navigate(route)
}
}
}
NavHost(navController, startDestination = Route.Home.route) {
Route.Video.let { route ->
composable(route.route, route.arguments, content = {
@@ -90,7 +98,7 @@ fun AppNavigation(
VideoScreen(
videoFeedView = videoFeedViewModel,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
scrollToTop = scrollToTop
)
@@ -108,7 +116,7 @@ fun AppNavigation(
SearchScreen(
searchFeedViewModel = searchFeedViewModel,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
scrollToTop = scrollToTop
)
@@ -128,7 +136,7 @@ fun AppNavigation(
homeFeedViewModel = homeFeedViewModel,
repliesFeedViewModel = repliesFeedViewModel,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
pagerState = homePagerState,
scrollToTop = scrollToTop,
nip47 = nip47
@@ -152,7 +160,7 @@ fun AppNavigation(
notifFeedViewModel = notifFeedViewModel,
userReactionsStatsModel = userReactionsStatsModel,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
scrollToTop = scrollToTop
)
@@ -163,16 +171,16 @@ fun AppNavigation(
})
}
composable(Route.Message.route, content = { ChatroomListScreen(accountViewModel, navController) })
composable(Route.BlockedUsers.route, content = { HiddenUsersScreen(accountViewModel, navController) })
composable(Route.Bookmarks.route, content = { BookmarkListScreen(accountViewModel, navController) })
composable(Route.Message.route, content = { ChatroomListScreen(accountViewModel, nav) })
composable(Route.BlockedUsers.route, content = { HiddenUsersScreen(accountViewModel, nav) })
composable(Route.Bookmarks.route, content = { BookmarkListScreen(accountViewModel, nav) })
Route.Profile.let { route ->
composable(route.route, route.arguments, content = {
ProfileScreen(
userId = it.arguments?.getString("id"),
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
})
}
@@ -182,7 +190,7 @@ fun AppNavigation(
ThreadScreen(
noteId = it.arguments?.getString("id"),
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
})
}
@@ -192,7 +200,7 @@ fun AppNavigation(
HashtagScreen(
tag = it.arguments?.getString("id"),
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
})
}
@@ -202,7 +210,7 @@ fun AppNavigation(
ChatroomScreen(
userId = it.arguments?.getString("id"),
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
})
}
@@ -212,7 +220,7 @@ fun AppNavigation(
ChannelScreen(
channelId = it.arguments?.getString("id"),
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
})
}
@@ -230,7 +238,7 @@ fun AppNavigation(
actionableNextPage?.let {
LaunchedEffect(it) {
navController.navigate(it)
nav(it)
}
actionableNextPage = null
}
@@ -83,7 +83,7 @@ import kotlinx.coroutines.withContext
@Composable
fun AppTopBar(followLists: FollowListViewModel, navController: NavHostController, scaffoldState: ScaffoldState, accountViewModel: AccountViewModel) {
when (currentRoute(navController)?.substringBefore("?")) {
// Route.Profile.route -> TopBarWithBackButton(navController)
// Route.Profile.route -> TopBarWithBackButton(nav)
Route.Home.base -> HomeTopBar(followLists, scaffoldState, accountViewModel)
Route.Video.base -> StoriesTopBar(followLists, scaffoldState, accountViewModel)
Route.Notification.base -> NotificationTopBar(followLists, scaffoldState, accountViewModel)
@@ -48,8 +48,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.BuildConfig
import com.vitorpamplona.amethyst.LocalPreferences
@@ -69,7 +67,7 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun DrawerContent(
navController: NavHostController,
nav: (String) -> Unit,
scaffoldState: ScaffoldState,
sheetState: ModalBottomSheetState,
accountViewModel: AccountViewModel
@@ -89,7 +87,7 @@ fun DrawerContent(
.padding(horizontal = 25.dp)
.padding(top = 100.dp),
scaffoldState,
navController
nav
)
Divider(
thickness = 0.25.dp,
@@ -97,7 +95,7 @@ fun DrawerContent(
)
ListContent(
account.userProfile().pubkeyHex,
navController,
nav,
scaffoldState,
sheetState,
modifier = Modifier
@@ -106,7 +104,7 @@ fun DrawerContent(
account
)
BottomContent(account.userProfile(), scaffoldState, navController)
BottomContent(account.userProfile(), scaffoldState, nav)
}
}
}
@@ -116,7 +114,7 @@ fun ProfileContent(
baseAccountUser: User,
modifier: Modifier = Modifier,
scaffoldState: ScaffoldState,
navController: NavController
nav: (String) -> Unit
) {
val coroutineScope = rememberCoroutineScope()
@@ -170,7 +168,7 @@ fun ProfileContent(
.border(3.dp, MaterialTheme.colors.background, CircleShape)
.background(MaterialTheme.colors.background)
.clickable(onClick = {
navController.navigate(route)
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
}
@@ -185,7 +183,7 @@ fun ProfileContent(
modifier = Modifier
.padding(top = 7.dp)
.clickable(onClick = {
navController.navigate(route)
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
}
@@ -203,7 +201,7 @@ fun ProfileContent(
.padding(top = 15.dp)
.clickable(
onClick = {
navController.navigate(route)
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
}
@@ -215,7 +213,7 @@ fun ProfileContent(
modifier = Modifier
.padding(top = 15.dp)
.clickable(onClick = {
navController.navigate(route)
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
}
@@ -244,7 +242,7 @@ fun ProfileContent(
@Composable
fun ListContent(
accountUserPubKey: String?,
navController: NavHostController,
nav: (String) -> Unit,
scaffoldState: ScaffoldState,
sheetState: ModalBottomSheetState,
modifier: Modifier,
@@ -263,7 +261,7 @@ fun ListContent(
title = stringResource(R.string.profile),
icon = Route.Profile.icon,
tint = MaterialTheme.colors.primary,
navController = navController,
nav = nav,
scaffoldState = scaffoldState,
route = "User/$accountUserPubKey"
)
@@ -272,7 +270,7 @@ fun ListContent(
title = stringResource(R.string.bookmarks),
icon = Route.Bookmarks.icon,
tint = MaterialTheme.colors.onBackground,
navController = navController,
nav = nav,
scaffoldState = scaffoldState,
route = Route.Bookmarks.route
)
@@ -282,7 +280,7 @@ fun ListContent(
title = stringResource(R.string.security_filters),
icon = Route.BlockedUsers.icon,
tint = MaterialTheme.colors.onBackground,
navController = navController,
nav = nav,
scaffoldState = scaffoldState,
route = Route.BlockedUsers.route
)
@@ -402,13 +400,13 @@ fun NavigationRow(
title: String,
icon: Int,
tint: Color,
navController: NavHostController,
nav: (String) -> Unit,
scaffoldState: ScaffoldState,
route: String
) {
val coroutineScope = rememberCoroutineScope()
IconRow(title, icon, tint, onClick = {
navController.navigate(route)
nav(route)
coroutineScope.launch {
scaffoldState.drawerState.close()
}
@@ -448,7 +446,7 @@ fun IconRow(title: String, icon: Int, tint: Color, onClick: () -> Unit, onLongCl
}
@Composable
fun BottomContent(user: User, scaffoldState: ScaffoldState, navController: NavController) {
fun BottomContent(user: User, scaffoldState: ScaffoldState, nav: (String) -> Unit) {
val coroutineScope = rememberCoroutineScope()
// store the dialog open or close state
@@ -515,7 +513,7 @@ fun BottomContent(user: User, scaffoldState: ScaffoldState, navController: NavCo
coroutineScope.launch {
scaffoldState.drawerState.close()
}
navController.navigate(it)
nav(it)
},
onClose = { dialogOpen = false }
)
@@ -1,8 +1,10 @@
package com.vitorpamplona.amethyst.ui.navigation
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavDestination
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.currentBackStackEntryAsState
@@ -185,3 +187,48 @@ object MessagesLatestItem {
return (note.createdAt() ?: 0) > lastTime
}
}
fun getRouteWithArguments(navController: NavHostController): String? {
val currentEntry = navController.currentBackStackEntry ?: return null
return getRouteWithArguments(currentEntry.destination, currentEntry.arguments)
}
private fun getRouteWithArguments(
destination: NavDestination,
arguments: Bundle?
): String? {
var route = destination.route ?: return null
arguments?.let { bundle ->
destination.arguments.keys.forEach { key ->
val value = destination.arguments[key]?.type?.get(bundle, key)?.toString()
if (value == null) {
val keyStart = route.indexOf("{$key}")
// if it is a parameter, removes the complete segment `var={key}` and adjust connectors `#`, `&` or `&`
if (keyStart > 0 && route[keyStart - 1] == '=') {
val end = keyStart + "{$key}".length
var start = keyStart
for (i in keyStart downTo 0) {
if (route[i] == '#' || route[i] == '?' || route[i] == '&') {
start = i + 1
break
}
}
if (end < route.length && route[end] == '&') {
route = route.removeRange(start, end + 1)
} else if (end < route.length && route[end] == '#') {
route = route.removeRange(start - 1, end)
} else if (end == route.length) {
route = route.removeRange(start - 1, end)
} else {
route = route.removeRange(start, end)
}
} else {
route = route.replaceFirst("{$key}", "")
}
} else {
route = route.replaceFirst("{$key}", value)
}
}
}
return route
}
@@ -32,7 +32,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.screen.BadgeCard
@@ -42,7 +41,7 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val noteState by likeSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
@@ -79,7 +78,7 @@ fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForL
routeFor(
note,
accountViewModel.userProfile()
)?.let { navController.navigate(it) }
)?.let { nav(it) }
}
},
onLongClick = { popupExpanded = true }
@@ -149,7 +148,7 @@ fun BadgeCompose(likeSetCard: BadgeCard, isInnerNote: Boolean = false, routeForL
isBoostedNote = true,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
@@ -17,7 +17,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
@@ -55,7 +54,7 @@ fun BlankNote(modifier: Modifier = Modifier, isQuote: Boolean = false, idHex: St
}
@Composable
fun HiddenNote(reports: Set<Note>, loggedIn: User, modifier: Modifier = Modifier, isQuote: Boolean = false, navController: NavController, onClick: () -> Unit) {
fun HiddenNote(reports: Set<Note>, loggedIn: User, modifier: Modifier = Modifier, isQuote: Boolean = false, nav: (String) -> Unit, onClick: () -> Unit) {
Column(modifier = modifier) {
Row(modifier = Modifier.padding(horizontal = if (!isQuote) 12.dp else 6.dp)) {
Column(modifier = Modifier.padding(start = if (!isQuote) 10.dp else 5.dp)) {
@@ -75,7 +74,7 @@ fun HiddenNote(reports: Set<Note>, loggedIn: User, modifier: Modifier = Modifier
reports.forEach {
NoteAuthorPicture(
baseNote = it,
navController = navController,
nav = nav,
userAccount = loggedIn,
size = 35.dp
)
@@ -25,7 +25,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R
@@ -37,7 +36,7 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val noteState by boostSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
@@ -77,7 +76,7 @@ fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, ro
routeFor(
note,
account.userProfile()
)?.let { navController.navigate(it) }
)?.let { nav(it) }
}
},
onLongClick = { popupExpanded = true }
@@ -114,7 +113,7 @@ fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, ro
boostSetCard.boostEvents.forEach {
NoteAuthorPicture(
baseNote = it,
navController = navController,
nav = nav,
userAccount = account.userProfile(),
size = 35.dp
)
@@ -128,7 +127,7 @@ fun BoostSetCompose(boostSetCard: BoostSetCard, isInnerNote: Boolean = false, ro
isBoostedNote = true,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel)
@@ -39,7 +39,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
@@ -57,7 +56,7 @@ import kotlinx.coroutines.launch
fun ChatroomCompose(
baseNote: Note,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteState by baseNote.live().metadata.observeAsState()
val note = noteState?.note
@@ -128,7 +127,7 @@ fun ChatroomCompose(
channelLastTime = note.createdAt(),
channelLastContent = "${author?.toBestDisplayName()}: " + description,
hasNewMessages = hasNewMessages,
onClick = { navController.navigate("Channel/${chan.idHex}") }
onClick = { nav("Channel/${chan.idHex}") }
)
}
} else {
@@ -172,7 +171,7 @@ fun ChatroomCompose(
channelLastTime = note.createdAt(),
channelLastContent = accountViewModel.decrypt(note),
hasNewMessages = hasNewMessages,
onClick = { navController.navigate("Room/${user.pubkeyHex}") }
onClick = { nav("Room/${user.pubkeyHex}") }
)
}
}
@@ -50,7 +50,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R
@@ -81,7 +80,7 @@ fun ChatroomMessageCompose(
innerQuote: Boolean = false,
parentBackgroundColor: Color? = null,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
onWantsToReply: (Note) -> Unit
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
@@ -137,7 +136,7 @@ fun ChatroomMessageCompose(
account.userProfile(),
Modifier,
innerQuote,
navController,
nav,
onClick = { showHiddenNote = true }
)
} else {
@@ -210,7 +209,7 @@ fun ChatroomMessageCompose(
.combinedClickable(
onClick = {
if (noteEvent is ChannelCreateEvent) {
navController.navigate("Channel/${note.idHex}")
nav("Channel/${note.idHex}")
}
},
onLongClick = { popupExpanded = true }
@@ -229,7 +228,7 @@ fun ChatroomMessageCompose(
DrawAuthorInfo(
baseNote,
alignment,
navController
nav
)
} else {
Spacer(modifier = Modifier.height(5.dp))
@@ -245,7 +244,7 @@ fun ChatroomMessageCompose(
innerQuote = true,
parentBackgroundColor = backgroundBubbleColor,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
onWantsToReply = onWantsToReply
)
}
@@ -269,7 +268,7 @@ fun ChatroomMessageCompose(
isAcceptableAndCanPreview.second,
backgroundBubbleColor,
accountViewModel,
navController
nav
)
}
}
@@ -363,7 +362,7 @@ private fun RenderRegularTextNote(
canPreview: Boolean,
backgroundBubbleColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val tags = remember { note.event?.tags() }
val eventContent = remember { accountViewModel.decrypt(note) }
@@ -371,23 +370,23 @@ private fun RenderRegularTextNote(
if (eventContent != null) {
TranslatableRichTextViewer(
eventContent,
canPreview,
modifier,
tags,
backgroundBubbleColor,
accountViewModel,
navController
content = eventContent,
canPreview = canPreview,
modifier = modifier,
tags = tags,
backgroundColor = backgroundBubbleColor,
accountViewModel = accountViewModel,
nav = nav
)
} else {
TranslatableRichTextViewer(
stringResource(R.string.could_not_decrypt_the_message),
true,
modifier,
tags,
backgroundBubbleColor,
accountViewModel,
navController
content = stringResource(id = R.string.could_not_decrypt_the_message),
canPreview = true,
modifier = modifier,
tags = tags,
backgroundColor = backgroundBubbleColor,
accountViewModel = accountViewModel,
nav = nav
)
}
}
@@ -444,7 +443,7 @@ private fun RenderCreateChannelNote(note: Note) {
private fun DrawAuthorInfo(
baseNote: Note,
alignment: Arrangement.Horizontal,
navController: NavController
nav: (String) -> Unit
) {
val userState by baseNote.author!!.live().metadata.observeAsState()
@@ -468,7 +467,7 @@ private fun DrawAuthorInfo(
.height(25.dp)
.clip(shape = CircleShape)
.clickable(onClick = {
navController.navigate(route)
nav(route)
})
)
@@ -479,7 +478,7 @@ private fun DrawAuthorInfo(
fontWeight = FontWeight.Bold,
overrideColor = MaterialTheme.colors.onBackground,
route = route,
navController = navController
nav = nav
)
}
}
@@ -25,7 +25,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R
@@ -37,7 +36,7 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val noteState by likeSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
@@ -76,7 +75,7 @@ fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, route
routeFor(
note,
account.userProfile()
)?.let { navController.navigate(it) }
)?.let { nav(it) }
}
},
onLongClick = { popupExpanded = true }
@@ -113,7 +112,7 @@ fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, route
likeSetCard.likeEvents.forEach {
NoteAuthorPicture(
baseNote = it,
navController = navController,
nav = nav,
userAccount = account.userProfile(),
size = 35.dp
)
@@ -127,7 +126,7 @@ fun LikeSetCompose(likeSetCard: LikeSetCard, isInnerNote: Boolean = false, route
isBoostedNote = true,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel)
@@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.compositeOver
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.ui.screen.MessageSetCard
@@ -35,7 +34,7 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MessageSetCompose(messageSetCard: MessageSetCard, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun MessageSetCompose(messageSetCard: MessageSetCard, routeForLastRead: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val noteState by messageSetCard.note.live().metadata.observeAsState()
val note = remember(noteState) { noteState?.note }
@@ -81,7 +80,7 @@ fun MessageSetCompose(messageSetCard: MessageSetCard, routeForLastRead: String,
routeFor(
note,
accountViewModel.userProfile()
)?.let { navController.navigate(it) }
)?.let { nav(it) }
}
},
onLongClick = { popupExpanded = true }
@@ -108,7 +107,7 @@ fun MessageSetCompose(messageSetCard: MessageSetCard, routeForLastRead: String,
addMarginTop = false,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel)
@@ -20,6 +20,7 @@ import androidx.compose.material.icons.filled.Bolt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
@@ -35,7 +36,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R
@@ -51,13 +51,15 @@ import com.vitorpamplona.amethyst.ui.screen.MultiSetCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.showAmountAxis
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.math.BigDecimal
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val baseNote = remember { multiSetCard.note }
val noteState by baseNote.live().metadata.observeAsState()
@@ -90,34 +92,38 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
val primaryColor = MaterialTheme.colors.primary.copy(0.12f)
val defaultBackgroundColor = MaterialTheme.colors.background
val backgroundColor = remember(isNew) {
if (isNew) {
primaryColor.compositeOver(defaultBackgroundColor)
} else {
defaultBackgroundColor
val backgroundColor by remember(isNew) {
derivedStateOf {
if (isNew) {
primaryColor.compositeOver(defaultBackgroundColor)
} else {
defaultBackgroundColor
}
}
}
val columnModifier = remember(isNew) {
Modifier
.background(backgroundColor)
.padding(
start = 12.dp,
end = 12.dp,
top = 10.dp
)
.combinedClickable(
onClick = {
scope.launch {
routeFor(
baseNote,
account.userProfile()
)?.let { navController.navigate(it) }
}
},
onLongClick = { popupExpanded = true }
)
.fillMaxWidth()
val columnModifier by remember(isNew, backgroundColor) {
derivedStateOf {
Modifier
.background(backgroundColor)
.padding(
start = 12.dp,
end = 12.dp,
top = 10.dp
)
.combinedClickable(
onClick = {
scope.launch {
routeFor(
baseNote,
account.userProfile()
)?.let { nav(it) }
}
},
onLongClick = { popupExpanded = true }
)
.fillMaxWidth()
}
}
val zapEvents = remember { multiSetCard.zapEvents }
@@ -126,15 +132,15 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
Column(modifier = columnModifier) {
if (zapEvents.isNotEmpty()) {
RenderZapGallery(zapEvents, navController, account, accountViewModel)
RenderZapGallery(zapEvents, backgroundColor, nav, account, accountViewModel)
}
if (boostEvents.isNotEmpty()) {
RenderBoostGallery(boostEvents, navController, account, accountViewModel)
RenderBoostGallery(boostEvents, backgroundColor, nav, account, accountViewModel)
}
if (likeEvents.isNotEmpty()) {
RenderLikeGallery(likeEvents, navController, account, accountViewModel)
RenderLikeGallery(likeEvents, backgroundColor, nav, account, accountViewModel)
}
Row(Modifier.fillMaxWidth()) {
@@ -147,7 +153,7 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
isBoostedNote = true,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel)
@@ -158,8 +164,9 @@ fun MultiSetCompose(multiSetCard: MultiSetCard, routeForLastRead: String, accoun
@Composable
private fun RenderLikeGallery(
likeEvents: List<Note>,
navController: NavController,
likeEvents: ImmutableList<Note>,
backgroundColor: Color,
nav: (String) -> Unit,
account: Account,
accountViewModel: AccountViewModel
) {
@@ -183,14 +190,15 @@ private fun RenderLikeGallery(
)
}
AuthorGallery(likeEvents, navController, account, accountViewModel)
AuthorGallery(likeEvents, backgroundColor, nav, account, accountViewModel)
}
}
@Composable
private fun RenderZapGallery(
zapEvents: Map<Note, Note>,
navController: NavController,
zapEvents: ImmutableMap<Note, Note>,
backgroundColor: Color,
nav: (String) -> Unit,
account: Account,
accountViewModel: AccountViewModel
) {
@@ -214,18 +222,23 @@ private fun RenderZapGallery(
)
}
AuthorGalleryZaps(zapEvents, navController, account, accountViewModel)
AuthorGalleryZaps(zapEvents, backgroundColor, nav, account, accountViewModel)
}
}
@Composable
private fun RenderBoostGallery(
boostEvents: List<Note>,
navController: NavController,
boostEvents: ImmutableList<Note>,
backgroundColor: Color,
nav: (String) -> Unit,
account: Account,
accountViewModel: AccountViewModel
) {
Row(Modifier.fillMaxWidth()) {
Row(
modifier = remember {
Modifier.fillMaxWidth()
}
) {
Box(
modifier = remember {
Modifier
@@ -245,14 +258,15 @@ private fun RenderBoostGallery(
)
}
AuthorGallery(boostEvents, navController, account, accountViewModel)
AuthorGallery(boostEvents, backgroundColor, nav, account, accountViewModel)
}
}
@Composable
fun AuthorGalleryZaps(
authorNotes: Map<Note, Note>,
navController: NavController,
authorNotes: ImmutableMap<Note, Note>,
backgroundColor: Color,
nav: (String) -> Unit,
account: Account,
accountViewModel: AccountViewModel
) {
@@ -261,7 +275,7 @@ fun AuthorGalleryZaps(
Column(modifier = Modifier.padding(start = 10.dp)) {
FlowRow() {
authorNotes.forEach {
AuthorPictureAndComment(it.key, it.value, navController, accountState, accountViewModel)
AuthorPictureAndComment(it.key, it.value, backgroundColor, nav, accountState, accountViewModel)
}
}
}
@@ -271,7 +285,8 @@ fun AuthorGalleryZaps(
private fun AuthorPictureAndComment(
zapRequest: Note,
zapEvent: Note?,
navController: NavController,
backgroundColor: Color,
nav: (String) -> Unit,
accountUser: State<UserState?>,
accountViewModel: AccountViewModel
) {
@@ -287,23 +302,39 @@ private fun AuthorPictureAndComment(
val amount = (zapEvent?.event as? LnZapEvent)?.amount
if (decryptedContent != null) {
val newAuthor = LocalCache.getOrCreateUser(decryptedContent.pubKey)
content = Triple(newAuthor, decryptedContent.content, amount)
content = Triple(newAuthor, decryptedContent.content.ifBlank { null }, amount)
} else {
if (!zapRequest.event?.content().isNullOrBlank() || amount != null) {
content = Triple(author, zapRequest.event?.content(), amount)
content = Triple(author, zapRequest.event?.content()?.ifBlank { null }, amount)
}
}
}
}
}
val modifier = remember(content.second) {
if (!content.second.isNullOrBlank()) {
Modifier
.fillMaxWidth()
.clickable {
nav("User/${author.pubkeyHex}")
}
} else {
Modifier.clickable {
nav("User/${author.pubkeyHex}")
}
}
}
AuthorPictureAndComment(
author = content.first,
comment = content.second,
amount = showAmountAxis(content.third),
navController = navController,
backgroundColor = backgroundColor,
nav = nav,
accountUser = accountUser,
accountViewModel = accountViewModel
accountViewModel = accountViewModel,
modifier = modifier
)
}
@@ -312,24 +343,12 @@ private fun AuthorPictureAndComment(
author: User,
comment: String?,
amount: String?,
navController: NavController,
backgroundColor: Color,
nav: (String) -> Unit,
accountUser: State<UserState?>,
accountViewModel: AccountViewModel
accountViewModel: AccountViewModel,
modifier: Modifier
) {
val modifier = remember(comment) {
if (!comment.isNullOrBlank()) {
Modifier
.fillMaxWidth()
.clickable {
navController.navigate("User/${author.pubkeyHex}")
}
} else {
Modifier.clickable {
navController.navigate("User/${author.pubkeyHex}")
}
}
}
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically
@@ -358,16 +377,16 @@ private fun AuthorPictureAndComment(
}
}
if (!comment.isNullOrBlank()) {
comment?.let {
Spacer(modifier = Modifier.width(5.dp))
TranslatableRichTextViewer(
content = comment,
content = it,
canPreview = true,
tags = null,
modifier = Modifier.weight(1f),
backgroundColor = MaterialTheme.colors.background,
backgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@@ -375,27 +394,26 @@ private fun AuthorPictureAndComment(
@Composable
fun AuthorGallery(
authorNotes: Collection<Note>,
navController: NavController,
authorNotes: ImmutableList<Note>,
backgroundColor: Color,
nav: (String) -> Unit,
account: Account,
accountViewModel: AccountViewModel
) {
val accountState = account.userProfile().live().follows.observeAsState()
val listToRender = remember {
Pair(
authorNotes.take(50).mapNotNull { it.author },
authorNotes.size
)
}
Column(modifier = Modifier.padding(start = 10.dp)) {
FlowRow() {
listToRender.first.forEach { author ->
AuthorPictureAndComment(author, null, null, navController, accountState, accountViewModel)
}
authorNotes.forEach { note ->
note.author?.let { user ->
val modifier = remember {
Modifier.clickable {
nav("User/${user.pubkeyHex}")
}
}
if (listToRender.second > 50) {
Text(" and ${listToRender.second - 50} others")
AuthorPictureAndComment(user, null, null, backgroundColor, nav, accountState, accountViewModel, modifier)
}
}
}
}
@@ -77,7 +77,6 @@ import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.graphics.get
import androidx.navigation.NavController
import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter
import com.google.accompanist.flowlayout.FlowRow
@@ -153,7 +152,7 @@ fun NoteCompose(
addMarginTop: Boolean = true,
parentBackgroundColor: Color? = null,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = remember(accountState) { accountState?.account } ?: return
@@ -212,11 +211,11 @@ fun NoteCompose(
account.userProfile(),
modifier,
isBoostedNote,
navController,
nav,
onClick = { showHiddenNote = true }
)
} else if ((noteEvent is ChannelCreateEvent || noteEvent is ChannelMetadataEvent) && baseChannel != null) {
ChannelHeader(baseChannel = baseChannel, account = account, navController = navController)
ChannelHeader(baseChannel = baseChannel, account = account, nav = nav)
} else if (noteEvent is BadgeDefinitionEvent) {
BadgeDisplay(baseNote = note)
} else if (noteEvent is FileHeaderEvent) {
@@ -267,11 +266,7 @@ fun NoteCompose(
onClick = {
scope.launch {
routeFor(note, loggedIn)?.let {
if (note.idHex != navController.currentBackStackEntry?.arguments?.getString("id")) {
navController.navigate(it)
} else {
Log.d("Amethyst-Navigation", "Note already exists in the backstack!")
}
nav(it)
}
}
},
@@ -292,7 +287,7 @@ fun NoteCompose(
}
) {
if (!isBoostedNote && !isQuotedNote) {
DrawAuthorImages(baseNote, loggedIn, navController)
DrawAuthorImages(baseNote, loggedIn, nav)
}
Column(
@@ -306,14 +301,14 @@ fun NoteCompose(
showAuthorPicture = isQuotedNote,
account = account,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
if (noteEvent !is RepostEvent && !makeItShort && !isQuotedNote) {
SecondUserInfoRow(
note,
account,
navController
nav
)
}
@@ -326,17 +321,17 @@ fun NoteCompose(
backgroundColor,
account,
accountViewModel,
navController
nav
)
}
when (noteEvent) {
is ReactionEvent -> {
RenderReaction(note, backgroundColor, accountViewModel, navController)
RenderReaction(note, backgroundColor, accountViewModel, nav)
}
is RepostEvent -> {
RenderRepost(note, backgroundColor, accountViewModel, navController)
RenderRepost(note, backgroundColor, accountViewModel, nav)
}
is ReportEvent -> {
@@ -344,31 +339,31 @@ fun NoteCompose(
}
is LongTextNoteEvent -> {
RenderLongFormContent(note, loggedIn, accountViewModel, navController)
RenderLongFormContent(note, loggedIn, accountViewModel, nav)
}
is BadgeAwardEvent -> {
RenderBadgeAward(note, backgroundColor, accountViewModel, navController)
RenderBadgeAward(note, backgroundColor, accountViewModel, nav)
}
is PeopleListEvent -> {
RenderPeopleList(noteState, backgroundColor, accountViewModel, navController)
RenderPeopleList(noteState, backgroundColor, accountViewModel, nav)
}
is AudioTrackEvent -> {
RenderAudioTrack(note, loggedIn, accountViewModel, navController)
RenderAudioTrack(note, loggedIn, accountViewModel, nav)
}
is PinListEvent -> {
RenderPinListEvent(noteState, backgroundColor, accountViewModel, navController)
RenderPinListEvent(noteState, backgroundColor, accountViewModel, nav)
}
is PrivateDmEvent -> {
RenderPrivateMessage(note, makeItShort, isAcceptableAndCanPreview.second, backgroundColor, accountViewModel, navController)
RenderPrivateMessage(note, makeItShort, isAcceptableAndCanPreview.second, backgroundColor, accountViewModel, nav)
}
is HighlightEvent -> {
RenderHighlight(note, makeItShort, isAcceptableAndCanPreview.second, backgroundColor, accountViewModel, navController)
RenderHighlight(note, makeItShort, isAcceptableAndCanPreview.second, backgroundColor, accountViewModel, nav)
}
is PollNoteEvent -> {
@@ -378,7 +373,7 @@ fun NoteCompose(
isAcceptableAndCanPreview.second,
backgroundColor,
accountViewModel,
navController
nav
)
}
@@ -389,7 +384,7 @@ fun NoteCompose(
isAcceptableAndCanPreview.second,
backgroundColor,
accountViewModel,
navController
nav
)
}
}
@@ -438,7 +433,7 @@ private fun RenderTextEvent(
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val tags = remember(note.event?.id()) { note.event?.tags() }
val hashtags = remember(note.event?.id()) { note.event?.hashtags() ?: emptyList() }
@@ -462,15 +457,15 @@ private fun RenderTextEvent(
tags = tags,
backgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
DisplayUncitedHashtags(hashtags, eventContent, navController)
DisplayUncitedHashtags(hashtags, eventContent, nav)
}
}
if (!makeItShort) {
ReactionsRow(note, accountViewModel, navController)
ReactionsRow(note, accountViewModel, nav)
}
Divider(
@@ -486,7 +481,7 @@ private fun RenderPoll(
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = note.event as? PollNoteEvent ?: return
val eventContent = noteEvent.content()
@@ -506,22 +501,22 @@ private fun RenderPoll(
noteEvent.tags(),
backgroundColor,
accountViewModel,
navController
nav
)
DisplayUncitedHashtags(noteEvent.hashtags(), eventContent, navController)
DisplayUncitedHashtags(noteEvent.hashtags(), eventContent, nav)
PollNote(
note,
canPreview = canPreview && !makeItShort,
backgroundColor,
accountViewModel,
navController
nav
)
}
if (!makeItShort) {
ReactionsRow(note, accountViewModel, navController)
ReactionsRow(note, accountViewModel, nav)
}
Divider(
@@ -537,7 +532,7 @@ private fun RenderHighlight(
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = note.event as? HighlightEvent ?: return
@@ -549,11 +544,11 @@ private fun RenderHighlight(
canPreview,
backgroundColor,
accountViewModel,
navController
nav
)
if (!makeItShort) {
ReactionsRow(note, accountViewModel, navController)
ReactionsRow(note, accountViewModel, nav)
}
Divider(
@@ -569,7 +564,7 @@ private fun RenderPrivateMessage(
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = note.event as? PrivateDmEvent ?: return
val withMe = remember { noteEvent.with(accountViewModel.userProfile().pubkeyHex) }
@@ -593,10 +588,10 @@ private fun RenderPrivateMessage(
tags = noteEvent.tags(),
backgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
DisplayUncitedHashtags(noteEvent.hashtags(), eventContent, navController)
DisplayUncitedHashtags(noteEvent.hashtags(), eventContent, nav)
}
}
} else {
@@ -613,12 +608,12 @@ private fun RenderPrivateMessage(
noteEvent.tags(),
backgroundColor,
accountViewModel,
navController
nav
)
}
if (!makeItShort) {
ReactionsRow(note, accountViewModel, navController)
ReactionsRow(note, accountViewModel, nav)
}
Divider(
@@ -632,12 +627,12 @@ fun RenderPeopleList(
noteState: NoteState?,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
DisplayPeopleList(noteState, backgroundColor, accountViewModel, navController)
DisplayPeopleList(noteState, backgroundColor, accountViewModel, nav)
noteState?.note?.let {
ReactionsRow(it, accountViewModel, navController)
ReactionsRow(it, accountViewModel, nav)
}
Divider(
@@ -651,7 +646,7 @@ fun DisplayPeopleList(
noteState: NoteState?,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val note = remember(noteState) { noteState?.note } ?: return
val noteEvent = note.event as? PeopleListEvent ?: return
@@ -696,7 +691,7 @@ fun DisplayPeopleList(
user,
overallModifier = Modifier,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@@ -740,7 +735,7 @@ private fun RenderBadgeAward(
note: Note,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
if (note.replyTo.isNullOrEmpty()) return
@@ -765,7 +760,7 @@ private fun RenderBadgeAward(
modifier = Modifier
.size(size = 35.dp)
.clickable {
navController.navigate("User/${user.pubkeyHex}")
nav("User/${user.pubkeyHex}")
},
verticalAlignment = Alignment.CenterVertically
) {
@@ -791,11 +786,11 @@ private fun RenderBadgeAward(
unPackReply = false,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
ReactionsRow(note, accountViewModel, navController)
ReactionsRow(note, accountViewModel, nav)
Divider(
modifier = Modifier.padding(top = 10.dp),
@@ -808,7 +803,7 @@ private fun RenderReaction(
note: Note,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
note.replyTo?.lastOrNull()?.let {
NoteCompose(
@@ -818,7 +813,7 @@ private fun RenderReaction(
unPackReply = false,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
@@ -836,7 +831,7 @@ private fun RenderRepost(
note: Note,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val boostedNote = remember {
note.replyTo?.lastOrNull()
@@ -850,7 +845,7 @@ private fun RenderRepost(
unPackReply = false,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@@ -860,13 +855,13 @@ private fun RenderPinListEvent(
noteState: NoteState?,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = noteState?.note?.event as? PinListEvent ?: return
PinListHeader(noteState, backgroundColor, accountViewModel, navController)
PinListHeader(noteState, backgroundColor, accountViewModel, nav)
ReactionsRow(noteState?.note, accountViewModel, navController)
ReactionsRow(noteState?.note, accountViewModel, nav)
Divider(
modifier = Modifier.padding(top = 10.dp),
@@ -879,7 +874,7 @@ fun PinListHeader(
noteState: NoteState?,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val note = remember(noteState) { noteState?.note } ?: return
val noteEvent = note.event as? PinListEvent ?: return
@@ -926,7 +921,7 @@ fun PinListHeader(
tags = emptyList(),
backgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@@ -970,13 +965,13 @@ private fun RenderAudioTrack(
note: Note,
loggedIn: User,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = note.event as? AudioTrackEvent ?: return
AudioTrackHeader(noteEvent, note, loggedIn, navController)
AudioTrackHeader(noteEvent, note, loggedIn, nav)
ReactionsRow(note, accountViewModel, navController)
ReactionsRow(note, accountViewModel, nav)
Divider(
modifier = Modifier.padding(top = 10.dp),
@@ -989,13 +984,13 @@ private fun RenderLongFormContent(
note: Note,
loggedIn: User,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = note.event as? LongTextNoteEvent ?: return
LongFormHeader(noteEvent, note, loggedIn)
ReactionsRow(note, accountViewModel, navController)
ReactionsRow(note, accountViewModel, nav)
Divider(
modifier = Modifier.padding(top = 10.dp),
@@ -1042,7 +1037,7 @@ private fun ReplyRow(
backgroundColor: Color,
account: Account,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = note.event
@@ -1068,16 +1063,16 @@ private fun ReplyRow(
parentBackgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.05f)
.compositeOver(backgroundColor),
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
} else {
ReplyInformation(note.replyTo, noteEvent.mentions(), account, navController)
ReplyInformation(note.replyTo, noteEvent.mentions(), account, nav)
}
Spacer(modifier = Modifier.height(5.dp))
} else if (noteEvent is ChannelMessageEvent && (note.replyTo != null || noteEvent.hasAnyTaggedUser())) {
note.channel()?.let {
ReplyInformationChannel(note.replyTo, noteEvent.mentions(), it, account, navController)
ReplyInformationChannel(note.replyTo, noteEvent.mentions(), it, account, nav)
}
Spacer(modifier = Modifier.height(5.dp))
@@ -1088,7 +1083,7 @@ private fun ReplyRow(
private fun SecondUserInfoRow(
note: Note,
account: Account,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = remember { note.event } ?: return
val noteAuthor = remember { note.author } ?: return
@@ -1098,7 +1093,7 @@ private fun SecondUserInfoRow(
val baseReward = remember { noteEvent.getReward() }
if (baseReward != null) {
DisplayReward(baseReward, note, account, navController)
DisplayReward(baseReward, note, account, nav)
}
val pow = remember { noteEvent.getPoWRank() }
@@ -1114,7 +1109,7 @@ private fun FirstUserInfoRow(
showAuthorPicture: Boolean,
account: Account,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
var moreActionsExpanded by remember { mutableStateOf(false) }
val eventNote = remember { baseNote.event } ?: return
@@ -1126,7 +1121,7 @@ private fun FirstUserInfoRow(
Row(verticalAlignment = Alignment.CenterVertically) {
if (showAuthorPicture) {
NoteAuthorPicture(baseNote, navController, loggedIn, 25.dp)
NoteAuthorPicture(baseNote, nav, loggedIn, 25.dp)
Spacer(padding)
NoteUsernameDisplay(baseNote, Modifier.weight(1f))
} else {
@@ -1140,7 +1135,7 @@ private fun FirstUserInfoRow(
color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
)
} else {
DisplayFollowingHashtagsInPost(eventNote, account, navController)
DisplayFollowingHashtagsInPost(eventNote, account, nav)
}
TimeAgo(time)
@@ -1188,17 +1183,17 @@ fun TimeAgo(time: Long) {
}
@Composable
private fun DrawAuthorImages(baseNote: Note, loggedIn: User, navController: NavController) {
private fun DrawAuthorImages(baseNote: Note, loggedIn: User, nav: (String) -> Unit) {
val baseChannel = remember { baseNote.channel() }
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, navController, loggedIn, 55.dp)
NoteAuthorPicture(baseNote, nav, loggedIn, 55.dp)
if (baseNote.event is RepostEvent) {
RepostNoteAuthorPicture(baseNote, navController, loggedIn)
RepostNoteAuthorPicture(baseNote, nav, loggedIn)
}
if (baseNote.event is ChannelMessageEvent && baseChannel != null) {
@@ -1262,7 +1257,7 @@ private fun ChannelNotePicture(baseChannel: Channel) {
@Composable
private fun RepostNoteAuthorPicture(
baseNote: Note,
navController: NavController,
nav: (String) -> Unit,
loggedIn: User
) {
val baseRepost = remember { baseNote.replyTo?.lastOrNull() }
@@ -1277,7 +1272,7 @@ private fun RepostNoteAuthorPicture(
Box(modifier) {
NoteAuthorPicture(
it,
navController,
nav,
loggedIn,
35.dp,
pictureModifier = Modifier.border(
@@ -1299,7 +1294,7 @@ fun DisplayHighlight(
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val quote =
remember {
@@ -1316,7 +1311,7 @@ fun DisplayHighlight(
emptyList(),
backgroundColor,
accountViewModel,
navController
nav
)
var userBase by remember { mutableStateOf<User?>(null) }
@@ -1343,7 +1338,7 @@ fun DisplayHighlight(
suffix = " ",
tags = userTags,
route = route,
navController = navController
nav = nav
)
}
}
@@ -1371,7 +1366,7 @@ fun DisplayHighlight(
fun DisplayFollowingHashtagsInPost(
noteEvent: EventInterface,
account: Account,
navController: NavController
nav: (String) -> Unit
) {
var firstTag by remember { mutableStateOf<String?>(null) }
@@ -1386,7 +1381,7 @@ fun DisplayFollowingHashtagsInPost(
firstTag?.let {
ClickableText(
text = AnnotatedString(" #$firstTag"),
onClick = { navController.navigate("Hashtag/$firstTag") },
onClick = { nav("Hashtag/$firstTag") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
alpha = 0.52f
@@ -1402,7 +1397,7 @@ fun DisplayFollowingHashtagsInPost(
fun DisplayUncitedHashtags(
hashtags: List<String>,
eventContent: String,
navController: NavController
nav: (String) -> Unit
) {
if (hashtags.isNotEmpty()) {
FlowRow(
@@ -1412,7 +1407,7 @@ fun DisplayUncitedHashtags(
if (!eventContent.contains(hashtag, true)) {
ClickableText(
text = AnnotatedString("#$hashtag "),
onClick = { navController.navigate("Hashtag/$hashtag") },
onClick = { nav("Hashtag/$hashtag") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
alpha = 0.52f
@@ -1444,7 +1439,7 @@ fun DisplayReward(
baseReward: BigDecimal,
baseNote: Note,
account: Account,
navController: NavController
nav: (String) -> Unit
) {
var popupExpanded by remember { mutableStateOf(false) }
@@ -1455,7 +1450,7 @@ fun DisplayReward(
) {
ClickableText(
text = AnnotatedString("#bounty"),
onClick = { navController.navigate("Hashtag/bounty") },
onClick = { nav("Hashtag/bounty") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colors.primary.copy(
alpha = 0.52f
@@ -1676,7 +1671,7 @@ fun FileStorageHeaderDisplay(baseNote: Note) {
}
@Composable
fun AudioTrackHeader(noteEvent: AudioTrackEvent, note: Note, loggedIn: User, navController: NavController) {
fun AudioTrackHeader(noteEvent: AudioTrackEvent, note: Note, loggedIn: User, nav: (String) -> Unit) {
val media = remember { noteEvent.media() }
val cover = remember { noteEvent.cover() }
val subject = remember { noteEvent.subject() }
@@ -1713,7 +1708,7 @@ fun AudioTrackHeader(noteEvent: AudioTrackEvent, note: Note, loggedIn: User, nav
modifier = Modifier
.padding(top = 5.dp, start = 10.dp, end = 10.dp)
.clickable {
navController.navigate("User/${it.second.pubkeyHex}")
nav("User/${it.second.pubkeyHex}")
}
) {
UserPicture(it.second, loggedIn, 25.dp)
@@ -1962,17 +1957,13 @@ private fun ShowMoreRelaysButton(onClick: () -> Unit) {
@Composable
fun NoteAuthorPicture(
baseNote: Note,
navController: NavController,
nav: (String) -> Unit,
userAccount: User,
size: Dp,
pictureModifier: Modifier = Modifier
) {
NoteAuthorPicture(baseNote, userAccount, size, pictureModifier) {
if (it.pubkeyHex != navController.currentBackStackEntry?.arguments?.getString("id")) {
navController.navigate("User/${it.pubkeyHex}")
} else {
Log.d("Amethyst-Navigation", "Profile destination already exists in the backstack!")
}
nav("User/${it.pubkeyHex}")
}
}
@@ -2019,17 +2010,13 @@ fun NoteAuthorPicture(
@Composable
fun UserPicture(
user: User,
navController: NavController,
nav: (String) -> Unit,
userAccount: User,
size: Dp,
pictureModifier: Modifier = Modifier
) {
UserPicture(user, userAccount, size, pictureModifier) {
if (it.pubkeyHex != navController.currentBackStackEntry?.arguments?.getString("id")) {
navController.navigate("User/${it.pubkeyHex}")
} else {
Log.d("Amethyst-Navigation", "Profile destination already exists in the backstack!")
}
nav("User/${it.pubkeyHex}")
}
}
@@ -29,7 +29,6 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.window.Popup
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
@@ -47,7 +46,7 @@ fun PollNote(
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = remember(accountState) { accountState?.account } ?: return
@@ -64,7 +63,7 @@ fun PollNote(
canPreview = canPreview,
backgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
@@ -75,7 +74,7 @@ fun PollNote(
canPreview: Boolean,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val zapsState by baseNote.live().zaps.observeAsState()
@@ -93,7 +92,7 @@ fun PollNote(
accountViewModel,
canPreview,
backgroundColor,
navController
nav
)
}
}
@@ -106,7 +105,7 @@ private fun OptionNote(
accountViewModel: AccountViewModel,
canPreview: Boolean,
backgroundColor: Color,
navController: NavController
nav: (String) -> Unit
) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -168,7 +167,7 @@ private fun OptionNote(
pollViewModel.pollEvent?.tags(),
backgroundColor,
accountViewModel,
navController
nav
)
}
}
@@ -202,7 +201,7 @@ private fun OptionNote(
pollViewModel.pollEvent?.tags(),
backgroundColor,
accountViewModel,
navController
nav
)
}
}
@@ -50,7 +50,6 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Popup
import androidx.navigation.NavController
import coil.compose.AsyncImage
import coil.request.CachePolicy
import coil.request.ImageRequest
@@ -66,7 +65,7 @@ import java.math.RoundingMode
import kotlin.math.roundToInt
@Composable
fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel, navController: NavController) {
fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = remember(accountState) { accountState?.account } ?: return
@@ -81,11 +80,11 @@ fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel, navControll
}
if (wantsToReplyTo != null) {
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, account, accountViewModel, navController)
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, account, accountViewModel, nav)
}
if (wantsToQuote != null) {
NewPostView({ wantsToQuote = null }, null, wantsToQuote, account, accountViewModel, navController)
NewPostView({ wantsToQuote = null }, null, wantsToQuote, account, accountViewModel, nav)
}
Spacer(modifier = Modifier.height(8.dp))
@@ -14,7 +14,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.*
@@ -23,7 +22,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun ReplyInformation(replyTo: List<Note>?, mentions: List<String>, account: Account, navController: NavController) {
fun ReplyInformation(replyTo: List<Note>?, mentions: List<String>, account: Account, nav: (String) -> Unit) {
var dupMentions by remember { mutableStateOf<List<User>?>(null) }
LaunchedEffect(Unit) {
@@ -34,7 +33,7 @@ fun ReplyInformation(replyTo: List<Note>?, mentions: List<String>, account: Acco
if (dupMentions != null) {
ReplyInformation(replyTo, dupMentions, account) {
navController.navigate("User/${it.pubkeyHex}")
nav("User/${it.pubkeyHex}")
}
}
}
@@ -116,7 +115,7 @@ fun ReplyInformation(replyTo: List<Note>?, dupMentions: List<User>?, account: Ac
}
@Composable
fun ReplyInformationChannel(replyTo: List<Note>?, mentions: List<String>, channel: Channel, account: Account, navController: NavController) {
fun ReplyInformationChannel(replyTo: List<Note>?, mentions: List<String>, channel: Channel, account: Account, nav: (String) -> Unit) {
var sortedMentions by remember { mutableStateOf<List<User>?>(null) }
LaunchedEffect(Unit) {
@@ -134,26 +133,26 @@ fun ReplyInformationChannel(replyTo: List<Note>?, mentions: List<String>, channe
sortedMentions,
channel,
onUserTagClick = {
navController.navigate("User/${it.pubkeyHex}")
nav("User/${it.pubkeyHex}")
},
onChannelTagClick = {
navController.navigate("Channel/${it.idHex}")
nav("Channel/${it.idHex}")
}
)
}
}
@Composable
fun ReplyInformationChannel(replyTo: List<Note>?, mentions: List<User>?, channel: Channel, navController: NavController) {
fun ReplyInformationChannel(replyTo: List<Note>?, mentions: List<User>?, channel: Channel, nav: (String) -> Unit) {
ReplyInformationChannel(
replyTo,
mentions,
channel,
onUserTagClick = {
navController.navigate("User/${it.pubkeyHex}")
nav("User/${it.pubkeyHex}")
},
onChannelTagClick = {
navController.navigate("Channel/${it.idHex}")
nav("Channel/${it.idHex}")
}
)
}
@@ -15,7 +15,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.FollowButton
@@ -34,7 +33,7 @@ fun UserCompose(
top = 10.dp
),
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -47,14 +46,14 @@ fun UserCompose(
Column(
modifier =
Modifier.clickable(
onClick = { navController.navigate("User/${baseUser.pubkeyHex}") }
onClick = { nav("User/${baseUser.pubkeyHex}") }
)
) {
Row(
modifier = overallModifier,
verticalAlignment = Alignment.CenterVertically
) {
UserPicture(baseUser, navController, account.userProfile(), 55.dp)
UserPicture(baseUser, nav, account.userProfile(), 55.dp)
Column(modifier = Modifier.padding(start = 10.dp).weight(1f)) {
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -26,7 +26,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController
import com.patrykandpatrick.vico.core.chart.composed.ComposedChartEntryModel
import com.patrykandpatrick.vico.core.entry.ChartEntryModel
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
@@ -54,7 +53,7 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter
@Composable
fun UserReactionsRow(model: UserReactionsViewModel, accountViewModel: AccountViewModel, navController: NavController, onClick: () -> Unit) {
fun UserReactionsRow(model: UserReactionsViewModel, accountViewModel: AccountViewModel, nav: (String) -> Unit, onClick: () -> Unit) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier
@@ -23,7 +23,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.model.LnZapEvent
@@ -37,7 +36,7 @@ import kotlinx.coroutines.launch
import java.math.BigDecimal
@Composable
fun ZapNoteCompose(baseNote: Pair<Note, Note>, accountViewModel: AccountViewModel, navController: NavController) {
fun ZapNoteCompose(baseNote: Pair<Note, Note>, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -60,7 +59,7 @@ fun ZapNoteCompose(baseNote: Pair<Note, Note>, accountViewModel: AccountViewMode
Column(
modifier =
Modifier.clickable(
onClick = { navController.navigate("User/${baseAuthor.pubkeyHex}") }
onClick = { nav("User/${baseAuthor.pubkeyHex}") }
),
verticalArrangement = Arrangement.Center
) {
@@ -73,7 +72,7 @@ fun ZapNoteCompose(baseNote: Pair<Note, Note>, accountViewModel: AccountViewMode
),
verticalAlignment = Alignment.CenterVertically
) {
UserPicture(baseAuthor, navController, account.userProfile(), 55.dp)
UserPicture(baseAuthor, nav, account.userProfile(), 55.dp)
Column(modifier = Modifier.padding(start = 10.dp).weight(1f)) {
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -26,7 +26,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R
@@ -39,7 +38,7 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ZapSetCompose(zapSetCard: ZapSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun ZapSetCompose(zapSetCard: ZapSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val noteState by zapSetCard.note.live().metadata.observeAsState()
val note = noteState?.note
@@ -78,7 +77,7 @@ fun ZapSetCompose(zapSetCard: ZapSetCard, isInnerNote: Boolean = false, routeFor
routeFor(
note,
account.userProfile()
)?.let { navController.navigate(it) }
)?.let { nav(it) }
}
},
onLongClick = { popupExpanded = true }
@@ -115,7 +114,7 @@ fun ZapSetCompose(zapSetCard: ZapSetCard, isInnerNote: Boolean = false, routeFor
zapSetCard.zapEvents.forEach {
NoteAuthorPicture(
baseNote = it.key,
navController = navController,
nav = nav,
userAccount = account.userProfile(),
size = 35.dp
)
@@ -129,7 +128,7 @@ fun ZapSetCompose(zapSetCard: ZapSetCard, isInnerNote: Boolean = false, routeFor
isBoostedNote = true,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel)
@@ -25,7 +25,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.R
@@ -37,7 +36,7 @@ import kotlinx.coroutines.withContext
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ZapUserSetCompose(zapSetCard: ZapUserSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, navController: NavController) {
fun ZapUserSetCompose(zapSetCard: ZapUserSetCard, isInnerNote: Boolean = false, routeForLastRead: String, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -61,7 +60,7 @@ fun ZapUserSetCompose(zapSetCard: ZapUserSetCard, isInnerNote: Boolean = false,
modifier = Modifier
.background(backgroundColor)
.clickable {
navController.navigate("User/${zapSetCard.user.pubkeyHex}")
nav("User/${zapSetCard.user.pubkeyHex}")
}
) {
Row(
@@ -95,14 +94,14 @@ fun ZapUserSetCompose(zapSetCard: ZapUserSetCard, isInnerNote: Boolean = false,
zapSetCard.zapEvents.forEach {
NoteAuthorPicture(
baseNote = it.key,
navController = navController,
nav = nav,
userAccount = account.userProfile(),
size = 35.dp
)
}
}
UserCompose(baseUser = zapSetCard.user, accountViewModel = accountViewModel, navController = navController)
UserCompose(baseUser = zapSetCard.user, accountViewModel = accountViewModel, nav = nav)
}
}
}
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.MainScreen
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginPage
@@ -21,10 +22,20 @@ fun AccountScreen(accountStateViewModel: AccountStateViewModel, startingPage: St
LoginPage(accountStateViewModel, isFirstLogin = true)
}
is AccountState.LoggedIn -> {
MainScreen(AccountViewModel(state.account), accountStateViewModel, startingPage)
val accountViewModel: AccountViewModel = viewModel(
key = state.account.userProfile().pubkeyHex,
factory = AccountViewModel.Factory(state.account)
)
MainScreen(accountViewModel, accountStateViewModel, startingPage)
}
is AccountState.LoggedInViewOnly -> {
MainScreen(AccountViewModel(state.account), accountStateViewModel, startingPage)
val accountViewModel: AccountViewModel = viewModel(
key = state.account.userProfile().pubkeyHex,
factory = AccountViewModel.Factory(state.account)
)
MainScreen(accountViewModel, accountStateViewModel, startingPage)
}
}
}
@@ -4,7 +4,10 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
@Immutable
abstract class Card() {
abstract fun createdAt(): Long
abstract fun id(): String
@@ -47,7 +50,7 @@ class ZapSetCard(val note: Note, val zapEvents: Map<Note, Note>) : Card() {
}
@Immutable
class ZapUserSetCard(val user: User, val zapEvents: Map<Note, Note>) : Card() {
class ZapUserSetCard(val user: User, val zapEvents: ImmutableMap<Note, Note>) : Card() {
val createdAt = zapEvents.maxOf { it.value.createdAt() ?: 0 }
override fun createdAt(): Long {
return createdAt
@@ -56,7 +59,7 @@ class ZapUserSetCard(val user: User, val zapEvents: Map<Note, Note>) : Card() {
}
@Immutable
class MultiSetCard(val note: Note, val boostEvents: List<Note>, val likeEvents: List<Note>, val zapEvents: Map<Note, Note>) : Card() {
class MultiSetCard(val note: Note, val boostEvents: ImmutableList<Note>, val likeEvents: ImmutableList<Note>, val zapEvents: ImmutableMap<Note, Note>) : Card() {
val createdAt = maxOf(
zapEvents.maxOfOrNull { it.value.createdAt() ?: 0 } ?: 0,
likeEvents.maxOfOrNull { it.createdAt() ?: 0 } ?: 0,
@@ -91,7 +94,7 @@ class MessageSetCard(val note: Note) : Card() {
sealed class CardFeedState {
object Loading : CardFeedState()
class Loaded(val feed: MutableState<List<Card>>) : CardFeedState()
class Loaded(val feed: MutableState<ImmutableList<Card>>) : CardFeedState()
object Empty : CardFeedState()
class FeedError(val errorMessage: String) : CardFeedState()
}
@@ -1,6 +1,5 @@
package com.vitorpamplona.amethyst.ui.screen
import android.util.Log
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
@@ -23,7 +22,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.ui.note.BadgeCompose
import com.vitorpamplona.amethyst.ui.note.BoostSetCompose
import com.vitorpamplona.amethyst.ui.note.LikeSetCompose
@@ -33,15 +31,13 @@ import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.note.ZapSetCompose
import com.vitorpamplona.amethyst.ui.note.ZapUserSetCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlin.time.ExperimentalTime
import kotlin.time.measureTimedValue
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun CardFeedView(
viewModel: CardFeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
routeForLastRead: String,
scrollStateKey: String? = null,
scrollToTop: Boolean = false
@@ -74,7 +70,7 @@ fun CardFeedView(
FeedLoaded(
state = state,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
routeForLastRead = routeForLastRead,
scrollStateKey = scrollStateKey,
scrollToTop = scrollToTop
@@ -91,12 +87,11 @@ fun CardFeedView(
}
}
@OptIn(ExperimentalTime::class)
@Composable
private fun FeedLoaded(
state: CardFeedState.Loaded,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
routeForLastRead: String,
scrollStateKey: String?,
scrollToTop: Boolean = false
@@ -121,64 +116,61 @@ private fun FeedLoaded(
state = listState
) {
itemsIndexed(state.feed.value, key = { _, item -> item.id() }) { _, item ->
val (value, elapsed) = measureTimedValue {
when (item) {
is NoteCard -> NoteCompose(
item.note,
isBoostedNote = false,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = routeForLastRead
)
is ZapSetCard -> ZapSetCompose(
item,
isInnerNote = false,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = routeForLastRead
)
is ZapUserSetCard -> ZapUserSetCompose(
item,
isInnerNote = false,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = routeForLastRead
)
is LikeSetCard -> LikeSetCompose(
item,
isInnerNote = false,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = routeForLastRead
)
is BoostSetCard -> BoostSetCompose(
item,
isInnerNote = false,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = routeForLastRead
)
is MultiSetCard -> MultiSetCompose(
item,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = routeForLastRead
)
is BadgeCard -> BadgeCompose(
item,
accountViewModel = accountViewModel,
navController = navController,
routeForLastRead = routeForLastRead
)
is MessageSetCard -> MessageSetCompose(
messageSetCard = item,
routeForLastRead = routeForLastRead,
accountViewModel = accountViewModel,
navController = navController
)
}
when (item) {
is NoteCard -> NoteCompose(
item.note,
isBoostedNote = false,
accountViewModel = accountViewModel,
nav = nav,
routeForLastRead = routeForLastRead
)
is ZapSetCard -> ZapSetCompose(
item,
isInnerNote = false,
accountViewModel = accountViewModel,
nav = nav,
routeForLastRead = routeForLastRead
)
is ZapUserSetCard -> ZapUserSetCompose(
item,
isInnerNote = false,
accountViewModel = accountViewModel,
nav = nav,
routeForLastRead = routeForLastRead
)
is LikeSetCard -> LikeSetCompose(
item,
isInnerNote = false,
accountViewModel = accountViewModel,
nav = nav,
routeForLastRead = routeForLastRead
)
is BoostSetCard -> BoostSetCompose(
item,
isInnerNote = false,
accountViewModel = accountViewModel,
nav = nav,
routeForLastRead = routeForLastRead
)
is MultiSetCard -> MultiSetCompose(
item,
accountViewModel = accountViewModel,
nav = nav,
routeForLastRead = routeForLastRead
)
is BadgeCard -> BadgeCompose(
item,
accountViewModel = accountViewModel,
nav = nav,
routeForLastRead = routeForLastRead
)
is MessageSetCard -> MessageSetCompose(
messageSetCard = item,
routeForLastRead = routeForLastRead,
accountViewModel = accountViewModel,
nav = nav
)
}
Log.d("Time", "${item.javaClass.simpleName} Feed in $elapsed ${item.id()}")
}
}
}
@@ -20,6 +20,9 @@ import com.vitorpamplona.amethyst.ui.components.BundledUpdate
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.FeedFilter
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -59,17 +62,28 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
if (newCards.isNotEmpty()) {
lastNotes = notes.toSet()
lastAccount = (localFilter as? NotificationFeedFilter)?.account
val singleList = (oldNotesState.feed.value + newCards)
val updatedCards = (oldNotesState.feed.value + newCards)
.distinctBy { it.id() }
.sortedWith(compareBy({ it.createdAt() }, { it.id() }))
.reversed()
.take(1000)
updateFeed(singleList)
.toImmutableList()
if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) {
updateFeed(updatedCards)
}
}
} else {
val cards = convertToCard(notes)
lastNotes = notes.toSet()
lastAccount = (localFilter as? NotificationFeedFilter)?.account
val cards = convertToCard(notes)
.sortedWith(compareBy({ it.createdAt() }, { it.id() }))
.reversed()
.take(1000)
.toImmutableList()
updateFeed(cards)
}
}
@@ -138,9 +152,9 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
singleList.chunked(50).map { chunk ->
MultiSetCard(
baseNote,
boostsInCard.filter { it in chunk },
reactionsInCard.filter { it in chunk },
zapsInCard.filter { it.value in chunk }
boostsInCard.filter { it in chunk }.toImmutableList(),
reactionsInCard.filter { it in chunk }.toImmutableList(),
zapsInCard.filter { it.value in chunk }.toImmutableMap()
)
}
}.flatten()
@@ -148,7 +162,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
val userZaps = zapsPerUser.map {
ZapUserSetCard(
it.key,
it.value
it.value.toImmutableMap()
)
}
@@ -165,7 +179,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
return (multiCards + textNoteCards + userZaps).sortedWith(compareBy({ it.createdAt() }, { it.id() })).reversed()
}
private fun updateFeed(notes: List<Card>) {
private fun updateFeed(notes: ImmutableList<Card>) {
val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch {
val currentState = _feedContent.value
@@ -173,7 +187,6 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
if (notes.isEmpty()) {
_feedContent.update { CardFeedState.Empty }
} else if (currentState is CardFeedState.Loaded) {
// updates the current list
currentState.feed.value = notes
} else {
_feedContent.update { CardFeedState.Loaded(mutableStateOf(notes)) }
@@ -207,8 +220,11 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
.sortedWith(compareBy({ it.createdAt() }, { it.id() }))
.reversed()
.take(1000)
.toImmutableList()
updateFeed(updatedCards)
if (!equalImmutableLists(oldNotesState.feed.value, updatedCards)) {
updateFeed(updatedCards)
}
}
} else {
// Refresh Everything
@@ -227,8 +243,8 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
}
private val bundlerInsert = BundledInsert<Set<Note>>(250, Dispatchers.IO)
fun invalidateData() {
bundler.invalidate()
fun invalidateData(ignoreIfDoing: Boolean = false) {
bundler.invalidate(ignoreIfDoing)
}
@OptIn(ExperimentalTime::class)
@@ -270,3 +286,13 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
super.onCleared()
}
}
fun <T> equalImmutableLists(list1: ImmutableList<T>, list2: ImmutableList<T>): Boolean {
if (list1.size != list2.size) return false
for (i in 0 until list1.size) {
if (list1[i] !== list2[i]) {
return false
}
}
return true
}
@@ -14,13 +14,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
fun ChatroomFeedView(viewModel: FeedViewModel, accountViewModel: AccountViewModel, navController: NavController, routeForLastRead: String, onWantsToReply: (Note) -> Unit) {
fun ChatroomFeedView(viewModel: FeedViewModel, accountViewModel: AccountViewModel, nav: (String) -> Unit, routeForLastRead: String, onWantsToReply: (Note) -> Unit) {
val feedState by viewModel.feedContent.collectAsState()
var isRefreshing by remember { mutableStateOf(false) }
@@ -63,7 +62,7 @@ fun ChatroomFeedView(viewModel: FeedViewModel, accountViewModel: AccountViewMode
state = listState
) {
itemsIndexed(state.feed.value, key = { _, item -> item.idHex }) { _, item ->
ChatroomMessageCompose(item, routeForLastRead, accountViewModel = accountViewModel, navController = navController, onWantsToReply = onWantsToReply)
ChatroomMessageCompose(item, routeForLastRead, accountViewModel = accountViewModel, nav = nav, onWantsToReply = onWantsToReply)
}
}
}
@@ -24,7 +24,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
@@ -36,7 +35,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
fun ChatroomListFeedView(
viewModel: FeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
markAsRead: MutableState<Boolean>
) {
val feedState by viewModel.feedContent.collectAsStateWithLifecycle()
@@ -68,7 +67,7 @@ fun ChatroomListFeedView(
if (refreshing) {
refreshing = false
}
FeedLoaded(state, accountViewModel, navController, markAsRead)
FeedLoaded(state, accountViewModel, nav, markAsRead)
}
FeedState.Loading -> {
@@ -86,7 +85,7 @@ fun ChatroomListFeedView(
private fun FeedLoaded(
state: FeedState.Loaded,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
markAsRead: MutableState<Boolean>
) {
val listState = rememberLazyListState()
@@ -139,7 +138,7 @@ private fun FeedLoaded(
ChatroomCompose(
item,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@@ -29,7 +29,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.note.NoteCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -39,7 +38,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
fun FeedView(
viewModel: FeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
routeForLastRead: String?,
scrollStateKey: String? = null,
scrollToTop: Boolean = false,
@@ -84,7 +83,7 @@ fun FeedView(
state,
routeForLastRead,
accountViewModel,
navController,
nav,
scrollStateKey,
scrollToTop
)
@@ -108,7 +107,7 @@ private fun FeedLoaded(
state: FeedState.Loaded,
routeForLastRead: String?,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
scrollStateKey: String?,
scrollToTop: Boolean = false
) {
@@ -142,7 +141,7 @@ private fun FeedLoaded(
modifier = baseModifier,
isBoostedNote = false,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@@ -21,7 +21,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.ui.note.ZapNoteCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -30,7 +29,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
fun LnZapFeedView(
viewModel: LnZapFeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
enablePullRefresh: Boolean = true
) {
val feedState by viewModel.feedContent.collectAsState()
@@ -64,7 +63,7 @@ fun LnZapFeedView(
refreshing = false
}
LnZapFeedLoaded(state, accountViewModel, navController)
LnZapFeedLoaded(state, accountViewModel, nav)
}
is LnZapFeedState.Loading -> {
LoadingFeed()
@@ -83,7 +82,7 @@ fun LnZapFeedView(
private fun LnZapFeedLoaded(
state: LnZapFeedState.Loaded,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val listState = rememberLazyListState()
@@ -95,7 +94,7 @@ private fun LnZapFeedLoaded(
state = listState
) {
itemsIndexed(state.feed.value, key = { _, item -> item.second.idHex }) { _, item ->
ZapNoteCompose(item, accountViewModel = accountViewModel, navController = navController)
ZapNoteCompose(item, accountViewModel = accountViewModel, nav = nav)
}
}
}
@@ -49,7 +49,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
@@ -82,7 +81,7 @@ import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ThreadFeedView(noteId: String, viewModel: FeedViewModel, accountViewModel: AccountViewModel, navController: NavController) {
fun ThreadFeedView(noteId: String, viewModel: FeedViewModel, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val feedState by viewModel.feedContent.collectAsState()
val listState = rememberLazyListState()
@@ -139,7 +138,7 @@ fun ThreadFeedView(noteId: String, viewModel: FeedViewModel, accountViewModel: A
if (item.idHex == noteId) MaterialTheme.colors.primary.copy(alpha = 0.52f) else MaterialTheme.colors.onSurface.copy(alpha = 0.32f)
),
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
} else {
Column() {
@@ -155,7 +154,7 @@ fun ThreadFeedView(noteId: String, viewModel: FeedViewModel, accountViewModel: A
isBoostedNote = false,
unPackReply = false,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@@ -204,7 +203,7 @@ fun NoteMaster(
baseNote: Note,
modifier: Modifier = Modifier,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteState by baseNote.live().metadata.observeAsState()
val note = noteState?.note
@@ -233,7 +232,7 @@ fun NoteMaster(
account.userProfile(),
Modifier,
false,
navController,
nav,
onClick = { showHiddenNote = true }
)
} else {
@@ -247,13 +246,13 @@ fun NoteMaster(
.padding(start = 12.dp, end = 12.dp)
.clickable(onClick = {
note.author?.let {
navController.navigate("User/${it.pubkeyHex}")
nav("User/${it.pubkeyHex}")
}
})
) {
NoteAuthorPicture(
baseNote = baseNote,
navController = navController,
nav = nav,
userAccount = account.userProfile(),
size = 55.dp
)
@@ -262,7 +261,7 @@ fun NoteMaster(
Row(verticalAlignment = Alignment.CenterVertically) {
NoteUsernameDisplay(baseNote, Modifier.weight(1f))
DisplayFollowingHashtagsInPost(noteEvent, account, navController)
DisplayFollowingHashtagsInPost(noteEvent, account, nav)
Text(
timeAgo(note.createdAt(), context = context),
@@ -290,7 +289,7 @@ fun NoteMaster(
val baseReward = noteEvent.getReward()
if (baseReward != null) {
DisplayReward(baseReward, baseNote, account, navController)
DisplayReward(baseReward, baseNote, account, nav)
}
val pow = noteEvent.getPoWRank()
@@ -353,11 +352,11 @@ fun NoteMaster(
) {
Column() {
if (noteEvent is PeopleListEvent) {
DisplayPeopleList(noteState, MaterialTheme.colors.background, accountViewModel, navController)
DisplayPeopleList(noteState, MaterialTheme.colors.background, accountViewModel, nav)
} else if (noteEvent is AudioTrackEvent) {
AudioTrackHeader(noteEvent, note, account.userProfile(), navController)
AudioTrackHeader(noteEvent, note, account.userProfile(), nav)
} else if (noteEvent is PinListEvent) {
PinListHeader(noteState, MaterialTheme.colors.background, accountViewModel, navController)
PinListHeader(noteState, MaterialTheme.colors.background, accountViewModel, nav)
} else if (noteEvent is HighlightEvent) {
DisplayHighlight(
noteEvent.quote(),
@@ -367,7 +366,7 @@ fun NoteMaster(
true,
backgroundColor,
accountViewModel,
navController
nav
)
} else {
val eventContent = note.event?.content()
@@ -384,10 +383,10 @@ fun NoteMaster(
note.event?.tags(),
MaterialTheme.colors.background,
accountViewModel,
navController
nav
)
DisplayUncitedHashtags(noteEvent.hashtags(), eventContent, navController)
DisplayUncitedHashtags(noteEvent.hashtags(), eventContent, nav)
if (noteEvent is PollNoteEvent) {
PollNote(
@@ -395,13 +394,13 @@ fun NoteMaster(
canPreview,
backgroundColor,
accountViewModel,
navController
nav
)
}
}
}
ReactionsRow(note, accountViewModel, navController)
ReactionsRow(note, accountViewModel, nav)
Divider(
modifier = Modifier.padding(top = 10.dp),
@@ -21,7 +21,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.ui.note.UserCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -30,7 +29,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
fun UserFeedView(
viewModel: UserFeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
enablePullRefresh: Boolean = true
) {
val feedState by viewModel.feedContent.collectAsState()
@@ -61,7 +60,7 @@ fun UserFeedView(
}
is UserFeedState.Loaded -> {
refreshing = false
FeedLoaded(state, accountViewModel, navController)
FeedLoaded(state, accountViewModel, nav)
}
is UserFeedState.Loading -> {
LoadingFeed()
@@ -80,7 +79,7 @@ fun UserFeedView(
private fun FeedLoaded(
state: UserFeedState.Loaded,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val listState = rememberLazyListState()
@@ -92,7 +91,7 @@ private fun FeedLoaded(
state = listState
) {
itemsIndexed(state.feed.value, key = { _, item -> item.pubkeyHex }) { _, item ->
UserCompose(item, accountViewModel = accountViewModel, navController = navController)
UserCompose(item, accountViewModel = accountViewModel, nav = nav)
}
}
}
@@ -3,9 +3,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.compose.runtime.Immutable
import androidx.core.content.ContextCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.R
@@ -23,6 +25,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.Locale
@Immutable
class AccountViewModel(private val account: Account) : ViewModel() {
val accountLiveData: LiveData<AccountState> = account.live.map { it }
val accountLanguagesLiveData: LiveData<AccountState> = account.liveLanguages.map { it }
@@ -228,4 +231,10 @@ class AccountViewModel(private val account: Account) : ViewModel() {
fun dontShowBlockAlertDialog() {
account.setHideBlockAlertDialog()
}
class Factory(val account: Account) : ViewModelProvider.Factory {
override fun <AccountViewModel : ViewModel> create(modelClass: Class<AccountViewModel>): AccountViewModel {
return AccountViewModel(account) as AccountViewModel
}
}
}
@@ -19,7 +19,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.dal.BookmarkPrivateFeedFilter
import com.vitorpamplona.amethyst.ui.dal.BookmarkPublicFeedFilter
@@ -30,7 +29,7 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun BookmarkListScreen(accountViewModel: AccountViewModel, navController: NavController) {
fun BookmarkListScreen(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account
@@ -74,8 +73,8 @@ fun BookmarkListScreen(accountViewModel: AccountViewModel, navController: NavCon
}
HorizontalPager(pageCount = 2, state = pagerState) { page ->
when (page) {
0 -> FeedView(privateFeedViewModel, accountViewModel, navController, null)
1 -> FeedView(publicFeedViewModel, accountViewModel, navController, null)
0 -> FeedView(privateFeedViewModel, accountViewModel, nav, null)
1 -> FeedView(publicFeedViewModel, accountViewModel, nav, null)
}
}
}
@@ -60,7 +60,6 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.Channel
@@ -83,7 +82,7 @@ import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
fun ChannelScreen(
channelId: String?,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account
@@ -143,7 +142,7 @@ fun ChannelScreen(
ChannelHeader(
channel,
account,
navController = navController
nav = nav
)
Column(
@@ -152,7 +151,7 @@ fun ChannelScreen(
.padding(vertical = 0.dp)
.weight(1f, true)
) {
ChatroomFeedView(feedViewModel, accountViewModel, navController, "Channel/$channelId") {
ChatroomFeedView(feedViewModel, accountViewModel, nav, "Channel/$channelId") {
replyTo.value = it
}
}
@@ -168,7 +167,7 @@ fun ChannelScreen(
null,
innerQuote = true,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
onWantsToReply = {
replyTo.value = it
}
@@ -248,7 +247,7 @@ fun ChannelScreen(
}
@Composable
fun ChannelHeader(baseChannel: Channel, account: Account, navController: NavController) {
fun ChannelHeader(baseChannel: Channel, account: Account, nav: (String) -> Unit) {
val channelState by baseChannel.live.observeAsState()
val channel = channelState?.channel ?: return
@@ -256,7 +255,7 @@ fun ChannelHeader(baseChannel: Channel, account: Account, navController: NavCont
Column(
Modifier.clickable {
navController.navigate("Channel/${baseChannel.idHex}")
nav("Channel/${baseChannel.idHex}")
}
) {
Column(modifier = Modifier.padding(12.dp)) {
@@ -306,9 +305,9 @@ fun ChannelHeader(baseChannel: Channel, account: Account, navController: NavCont
}
if (account.followingChannels.contains(channel.idHex)) {
LeaveButton(account, channel, navController)
LeaveButton(account, channel, nav)
} else {
JoinButton(account, channel, navController)
JoinButton(account, channel, nav)
}
}
}
@@ -386,7 +385,7 @@ private fun EditButton(account: Account, channel: Channel) {
}
@Composable
private fun JoinButton(account: Account, channel: Channel, navController: NavController) {
private fun JoinButton(account: Account, channel: Channel, nav: (String) -> Unit) {
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = {
@@ -404,12 +403,12 @@ private fun JoinButton(account: Account, channel: Channel, navController: NavCon
}
@Composable
private fun LeaveButton(account: Account, channel: Channel, navController: NavController) {
private fun LeaveButton(account: Account, channel: Channel, nav: (String) -> Unit) {
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = {
account.leaveChannel(channel.idHex)
navController.navigate(Route.Message.route)
nav(Route.Message.route)
},
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults
@@ -39,7 +39,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
import com.vitorpamplona.amethyst.ui.dal.ChatroomListKnownFeedFilter
@@ -52,7 +51,7 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ChatroomListScreen(accountViewModel: AccountViewModel, navController: NavController) {
fun ChatroomListScreen(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val pagerState = rememberPagerState()
val coroutineScope = rememberCoroutineScope()
@@ -151,7 +150,7 @@ fun ChatroomListScreen(accountViewModel: AccountViewModel, navController: NavCon
ChatroomListFeedView(
viewModel = tabs[page].viewModel,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
markAsRead = tabs[page].markAsRead
)
}
@@ -44,7 +44,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
@@ -61,7 +60,7 @@ import com.vitorpamplona.amethyst.ui.screen.ChatroomFeedView
import com.vitorpamplona.amethyst.ui.screen.NostrChatRoomFeedViewModel
@Composable
fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navController: NavController) {
fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account
val context = LocalContext.current
@@ -106,7 +105,7 @@ fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navContr
Column(Modifier.fillMaxHeight()) {
NostrChatroomDataSource.withUser?.let {
ChatroomHeader(it, account.userProfile(), navController = navController)
ChatroomHeader(it, account.userProfile(), nav = nav)
}
Column(
@@ -115,7 +114,7 @@ fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navContr
.padding(vertical = 0.dp)
.weight(1f, true)
) {
ChatroomFeedView(feedViewModel, accountViewModel, navController, "Room/$userId") {
ChatroomFeedView(feedViewModel, accountViewModel, nav, "Room/$userId") {
replyTo.value = it
}
}
@@ -131,7 +130,7 @@ fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navContr
null,
innerQuote = true,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
onWantsToReply = {
replyTo.value = it
}
@@ -208,10 +207,10 @@ fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navContr
}
@Composable
fun ChatroomHeader(baseUser: User, accountUser: User, navController: NavController) {
fun ChatroomHeader(baseUser: User, accountUser: User, nav: (String) -> Unit) {
Column(
modifier = Modifier.clickable(
onClick = { navController.navigate("User/${baseUser.pubkeyHex}") }
onClick = { nav("User/${baseUser.pubkeyHex}") }
)
) {
Column(modifier = Modifier.padding(12.dp)) {
@@ -22,7 +22,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.NostrHashtagDataSource
import com.vitorpamplona.amethyst.ui.dal.HashtagFeedFilter
@@ -32,7 +31,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun HashtagScreen(tag: String?, accountViewModel: AccountViewModel, navController: NavController) {
fun HashtagScreen(tag: String?, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -76,7 +75,7 @@ fun HashtagScreen(tag: String?, accountViewModel: AccountViewModel, navControlle
modifier = Modifier.padding(vertical = 0.dp)
) {
HashtagHeader(tag, account)
FeedView(feedViewModel, accountViewModel, navController, null)
FeedView(feedViewModel, accountViewModel, nav, null)
}
}
}
@@ -18,7 +18,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.dal.HiddenAccountsFeedFilter
import com.vitorpamplona.amethyst.ui.screen.NostrHiddenAccountsFeedViewModel
@@ -27,7 +26,7 @@ import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun HiddenUsersScreen(accountViewModel: AccountViewModel, navController: NavController) {
fun HiddenUsersScreen(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account
@@ -55,7 +54,7 @@ fun HiddenUsersScreen(accountViewModel: AccountViewModel, navController: NavCont
}
HorizontalPager(pageCount = 1, state = pagerState) { page ->
when (page) {
0 -> UserFeedView(feedViewModel, accountViewModel, navController)
0 -> UserFeedView(feedViewModel, accountViewModel, nav)
}
}
}
@@ -25,7 +25,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
import com.vitorpamplona.amethyst.ui.dal.HomeConversationsFeedFilter
@@ -45,7 +44,7 @@ fun HomeScreen(
homeFeedViewModel: NostrHomeFeedViewModel,
repliesFeedViewModel: NostrHomeRepliesFeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
pagerState: PagerState,
scrollToTop: Boolean = false,
nip47: String? = null
@@ -120,7 +119,7 @@ fun HomeScreen(
FeedView(
viewModel = tabs[page].viewModel,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
routeForLastRead = tabs[page].routeForLastRead,
scrollStateKey = tabs[page].scrollStateKey,
scrollToTop = scrollToTop
@@ -53,6 +53,14 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
skipHalfExpanded = true
)
val nav = remember {
{ route: String ->
if (getRouteWithArguments(navController) != route) {
navController.navigate(route)
}
}
}
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = remember(accountState) { accountState?.account }
@@ -76,7 +84,7 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
AppTopBar(followLists, navController, scaffoldState, accountViewModel)
},
drawerContent = {
DrawerContent(navController, scaffoldState, sheetState, accountViewModel)
DrawerContent(nav, scaffoldState, sheetState, accountViewModel)
BackHandler(enabled = scaffoldState.drawerState.isOpen) {
scope.launch { scaffoldState.drawerState.close() }
}
@@ -97,6 +105,14 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
fun FloatingButtons(navController: NavHostController, accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel) {
val accountState by accountStateViewModel.accountContent.collectAsState()
val nav = remember {
{ route: String ->
if (getRouteWithArguments(navController) != route) {
navController.navigate(route)
}
}
}
Crossfade(targetState = accountState, animationSpec = tween(durationMillis = 100)) { state ->
when (state) {
is AccountState.LoggedInViewOnly -> {
@@ -107,13 +123,13 @@ fun FloatingButtons(navController: NavHostController, accountViewModel: AccountV
}
is AccountState.LoggedIn -> {
if (currentRoute(navController)?.substringBefore("?") == Route.Home.base) {
NewNoteButton(state.account, accountViewModel, navController)
NewNoteButton(state.account, accountViewModel, nav)
}
if (currentRoute(navController) == Route.Message.base) {
ChannelFabColumn(state.account, navController)
ChannelFabColumn(state.account, nav)
}
if (currentRoute(navController)?.substringBefore("?") == Route.Video.base) {
NewImageButton(accountViewModel, navController)
NewImageButton(accountViewModel, nav)
}
}
}
@@ -23,7 +23,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis
import com.patrykandpatrick.vico.compose.axis.vertical.endAxis
import com.patrykandpatrick.vico.compose.axis.vertical.startAxis
@@ -61,7 +60,7 @@ fun NotificationScreen(
notifFeedViewModel: NotificationViewModel,
userReactionsStatsModel: UserReactionsViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
scrollToTop: Boolean = false
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
@@ -74,8 +73,6 @@ fun NotificationScreen(
LaunchedEffect(account.userProfile().pubkeyHex, account.defaultNotificationFollowList) {
NostrAccountDataSource.resetFilters()
NotificationFeedFilter.account = account
notifFeedViewModel.clear()
notifFeedViewModel.invalidateData()
}
val lifeCycleOwner = LocalLifecycleOwner.current
@@ -83,7 +80,7 @@ fun NotificationScreen(
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
NotificationFeedFilter.account = account
notifFeedViewModel.invalidateData()
notifFeedViewModel.invalidateData(true)
}
}
@@ -97,11 +94,11 @@ fun NotificationScreen(
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
SummaryBar(userReactionsStatsModel, accountViewModel, navController)
SummaryBar(userReactionsStatsModel, accountViewModel, nav)
CardFeedView(
viewModel = notifFeedViewModel,
accountViewModel = accountViewModel,
navController = navController,
nav = nav,
routeForLastRead = Route.Notification.base,
scrollStateKey = ScrollStateKeys.NOTIFICATION_SCREEN,
scrollToTop = scrollToTop
@@ -111,12 +108,12 @@ fun NotificationScreen(
}
@Composable
fun SummaryBar(model: UserReactionsViewModel, accountViewModel: AccountViewModel, navController: NavController) {
fun SummaryBar(model: UserReactionsViewModel, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
var showChart by remember {
mutableStateOf(false)
}
UserReactionsRow(model, accountViewModel, navController) {
UserReactionsRow(model, accountViewModel, nav) {
showChart = !showChart
}
@@ -49,7 +49,6 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
@@ -103,7 +102,7 @@ import kotlinx.coroutines.withContext
import java.math.BigDecimal
@Composable
fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navController: NavController) {
fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
if (userId == null) return
var userBase by remember { mutableStateOf<User?>(null) }
@@ -118,14 +117,14 @@ fun ProfileScreen(userId: String?, accountViewModel: AccountViewModel, navContro
ProfileScreen(
user = it,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController: NavController) {
fun ProfileScreen(user: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = remember(accountState) { accountState?.account } ?: return
@@ -210,7 +209,7 @@ fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController:
.fillMaxHeight()
) {
Column(modifier = Modifier.padding()) {
ProfileHeader(baseUser, navController, account, accountViewModel)
ProfileHeader(baseUser, nav, account, accountViewModel)
ScrollableTabRow(
backgroundColor = MaterialTheme.colors.background,
selectedTabIndex = pagerState.currentPage,
@@ -246,13 +245,13 @@ fun ProfileScreen(user: User, accountViewModel: AccountViewModel, navController:
}
) { page ->
when (page) {
0 -> TabNotesNewThreads(accountViewModel, navController)
1 -> TabNotesConversations(accountViewModel, navController)
2 -> TabFollows(baseUser, accountViewModel, navController)
3 -> TabFollowers(baseUser, accountViewModel, navController)
4 -> TabReceivedZaps(baseUser, accountViewModel, navController)
5 -> TabBookmarks(baseUser, accountViewModel, navController)
6 -> TabReports(baseUser, accountViewModel, navController)
0 -> TabNotesNewThreads(accountViewModel, nav)
1 -> TabNotesConversations(accountViewModel, nav)
2 -> TabFollows(baseUser, accountViewModel, nav)
3 -> TabFollowers(baseUser, accountViewModel, nav)
4 -> TabReceivedZaps(baseUser, accountViewModel, nav)
5 -> TabBookmarks(baseUser, accountViewModel, nav)
6 -> TabReports(baseUser, accountViewModel, nav)
7 -> TabRelays(baseUser, accountViewModel)
}
}
@@ -331,7 +330,7 @@ private fun FollowTabHeader(baseUser: User) {
@Composable
private fun ProfileHeader(
baseUser: User,
navController: NavController,
nav: (String) -> Unit,
account: Account,
accountViewModel: AccountViewModel
) {
@@ -412,7 +411,7 @@ private fun ProfileHeader(
.height(35.dp)
.padding(bottom = 3.dp)
) {
MessageButton(baseUser, navController)
MessageButton(baseUser, nav)
// No need for this button anymore
// NPubCopyButton(baseUser)
@@ -421,7 +420,7 @@ private fun ProfileHeader(
}
}
DrawAdditionalInfo(baseUser, account, accountViewModel, navController)
DrawAdditionalInfo(baseUser, account, accountViewModel, nav)
Divider(modifier = Modifier.padding(top = 6.dp))
}
@@ -468,7 +467,7 @@ private fun ProfileActions(
}
@Composable
private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewModel: AccountViewModel, navController: NavController) {
private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val userState by baseUser.live().metadata.observeAsState()
val user = remember(userState) { userState?.user } ?: return
val tags = remember(userState) { userState?.user?.info?.latestMetadata?.tags }
@@ -534,7 +533,7 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
user,
onScan = {
dialogOpen = false
navController.navigate(it)
nav(it)
},
onClose = { dialogOpen = false }
)
@@ -553,7 +552,7 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
}
}
DisplayBadges(baseUser, navController)
DisplayBadges(baseUser, nav)
DisplayNip05ProfileStatus(user)
@@ -613,7 +612,7 @@ private fun DrawAdditionalInfo(baseUser: User, account: Account, accountViewMode
tags = null,
backgroundColor = MaterialTheme.colors.background,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
}
@@ -701,7 +700,7 @@ private fun DisplayLNAddress(
@OptIn(ExperimentalLayoutApi::class)
private fun DisplayBadges(
baseUser: User,
navController: NavController
nav: (String) -> Unit
) {
val userBadgeState by baseUser.live().badges.observeAsState()
val userBadge = remember(userBadgeState) { userBadgeState?.user } ?: return
@@ -716,7 +715,7 @@ private fun DisplayBadges(
val baseBadgeDefinition = badgeAwardState?.note?.replyTo?.firstOrNull()
if (baseBadgeDefinition != null) {
BadgeThumb(baseBadgeDefinition, navController, 35.dp)
BadgeThumb(baseBadgeDefinition, nav, 35.dp)
}
}
}
@@ -728,12 +727,12 @@ private fun DisplayBadges(
@Composable
fun BadgeThumb(
note: Note,
navController: NavController,
nav: (String) -> Unit,
size: Dp,
pictureModifier: Modifier = Modifier
) {
BadgeThumb(note, size, pictureModifier) {
navController.navigate("Note/${it.idHex}")
nav("Note/${it.idHex}")
}
}
@@ -831,7 +830,7 @@ private fun DrawBanner(baseUser: User) {
}
@Composable
fun TabNotesNewThreads(accountViewModel: AccountViewModel, navController: NavController) {
fun TabNotesNewThreads(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val feedViewModel: NostrUserProfileNewThreadsFeedViewModel = viewModel()
LaunchedEffect(Unit) {
@@ -842,13 +841,13 @@ fun TabNotesNewThreads(accountViewModel: AccountViewModel, navController: NavCon
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
FeedView(feedViewModel, accountViewModel, nav, null, enablePullRefresh = false)
}
}
}
@Composable
fun TabNotesConversations(accountViewModel: AccountViewModel, navController: NavController) {
fun TabNotesConversations(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val feedViewModel: NostrUserProfileConversationsFeedViewModel = viewModel()
LaunchedEffect(Unit) {
@@ -859,13 +858,13 @@ fun TabNotesConversations(accountViewModel: AccountViewModel, navController: Nav
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
FeedView(feedViewModel, accountViewModel, nav, null, enablePullRefresh = false)
}
}
}
@Composable
fun TabBookmarks(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
fun TabBookmarks(baseUser: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val feedViewModel: NostrUserProfileBookmarksFeedViewModel = viewModel()
LaunchedEffect(Unit) {
@@ -876,13 +875,13 @@ fun TabBookmarks(baseUser: User, accountViewModel: AccountViewModel, navControll
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
FeedView(feedViewModel, accountViewModel, nav, null, enablePullRefresh = false)
}
}
}
@Composable
fun TabFollows(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
fun TabFollows(baseUser: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val feedViewModel: NostrUserProfileFollowsUserFeedViewModel = viewModel()
val userState by baseUser.live().follows.observeAsState()
@@ -895,13 +894,13 @@ fun TabFollows(baseUser: User, accountViewModel: AccountViewModel, navController
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
UserFeedView(feedViewModel, accountViewModel, navController, enablePullRefresh = false)
UserFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false)
}
}
}
@Composable
fun TabFollowers(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
fun TabFollowers(baseUser: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val feedViewModel: NostrUserProfileFollowersUserFeedViewModel = viewModel()
val userState by baseUser.live().follows.observeAsState()
@@ -914,13 +913,13 @@ fun TabFollowers(baseUser: User, accountViewModel: AccountViewModel, navControll
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
UserFeedView(feedViewModel, accountViewModel, navController, enablePullRefresh = false)
UserFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false)
}
}
}
@Composable
fun TabReceivedZaps(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
fun TabReceivedZaps(baseUser: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val feedViewModel: NostrUserProfileZapsFeedViewModel = viewModel()
val userState by baseUser.live().zaps.observeAsState()
@@ -933,13 +932,13 @@ fun TabReceivedZaps(baseUser: User, accountViewModel: AccountViewModel, navContr
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
LnZapFeedView(feedViewModel, accountViewModel, navController, enablePullRefresh = false)
LnZapFeedView(feedViewModel, accountViewModel, nav, enablePullRefresh = false)
}
}
}
@Composable
fun TabReports(baseUser: User, accountViewModel: AccountViewModel, navController: NavController) {
fun TabReports(baseUser: User, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val feedViewModel: NostrUserProfileReportFeedViewModel = viewModel()
val userState by baseUser.live().reports.observeAsState()
@@ -952,7 +951,7 @@ fun TabReports(baseUser: User, accountViewModel: AccountViewModel, navController
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
FeedView(feedViewModel, accountViewModel, navController, null, enablePullRefresh = false)
FeedView(feedViewModel, accountViewModel, nav, null, enablePullRefresh = false)
}
}
}
@@ -993,12 +992,12 @@ fun TabRelays(user: User, accountViewModel: AccountViewModel) {
}
@Composable
private fun MessageButton(user: User, navController: NavController) {
private fun MessageButton(user: User, nav: (String) -> Unit) {
Button(
modifier = Modifier
.padding(horizontal = 3.dp)
.width(50.dp),
onClick = { navController.navigate("Room/${user.pubkeyHex}") },
onClick = { nav("Room/${user.pubkeyHex}") },
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults
.buttonColors(
@@ -48,7 +48,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.Channel
@@ -83,7 +82,7 @@ import kotlinx.coroutines.channels.Channel as CoroutineChannel
fun SearchScreen(
searchFeedViewModel: NostrGlobalFeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
scrollToTop: Boolean = false
) {
val lifeCycleOwner = LocalLifecycleOwner.current
@@ -124,8 +123,8 @@ fun SearchScreen(
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
SearchBar(accountViewModel, navController)
FeedView(searchFeedViewModel, accountViewModel, navController, null, ScrollStateKeys.GLOBAL_SCREEN, scrollToTop)
SearchBar(accountViewModel, nav)
FeedView(searchFeedViewModel, accountViewModel, nav, null, ScrollStateKeys.GLOBAL_SCREEN, scrollToTop)
}
}
}
@@ -185,7 +184,7 @@ class SearchBarViewModel : ViewModel() {
@OptIn(FlowPreview::class)
@Composable
private fun SearchBar(accountViewModel: AccountViewModel, navController: NavController) {
private fun SearchBar(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val searchBarViewModel: SearchBarViewModel = viewModel()
searchBarViewModel.account = accountViewModel.accountLiveData.value?.account
@@ -306,12 +305,12 @@ private fun SearchBar(accountViewModel: AccountViewModel, navController: NavCont
) {
itemsIndexed(searchBarViewModel.hashtagResults.value, key = { _, item -> "#" + item }) { _, item ->
HashtagLine(item) {
navController.navigate("Hashtag/$item")
nav("Hashtag/$item")
}
}
itemsIndexed(searchBarViewModel.searchResults.value, key = { _, item -> "u" + item.pubkeyHex }) { _, item ->
UserCompose(item, accountViewModel = accountViewModel, navController = navController)
UserCompose(item, accountViewModel = accountViewModel, nav = nav)
}
itemsIndexed(searchBarViewModel.searchResultsChannels.value, key = { _, item -> "c" + item.idHex }) { _, item ->
@@ -327,12 +326,12 @@ private fun SearchBar(accountViewModel: AccountViewModel, navController: NavCont
channelLastTime = null,
channelLastContent = item.info.about,
false,
onClick = { navController.navigate("Channel/${item.idHex}") }
onClick = { nav("Channel/${item.idHex}") }
)
}
itemsIndexed(searchBarViewModel.searchResultsNotes.value, key = { _, item -> "n" + item.idHex }) { _, item ->
NoteCompose(item, accountViewModel = accountViewModel, navController = navController)
NoteCompose(item, accountViewModel = accountViewModel, nav = nav)
}
}
}
@@ -14,14 +14,13 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
import com.vitorpamplona.amethyst.ui.dal.ThreadFeedFilter
import com.vitorpamplona.amethyst.ui.screen.NostrThreadFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.ThreadFeedView
@Composable
fun ThreadScreen(noteId: String?, accountViewModel: AccountViewModel, navController: NavController) {
fun ThreadScreen(noteId: String?, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val account by accountViewModel.accountLiveData.observeAsState()
val lifeCycleOwner = LocalLifecycleOwner.current
@@ -63,7 +62,7 @@ fun ThreadScreen(noteId: String?, accountViewModel: AccountViewModel, navControl
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
ThreadFeedView(noteId, feedViewModel, accountViewModel, navController)
ThreadFeedView(noteId, feedViewModel, accountViewModel, nav)
}
}
}
@@ -59,7 +59,6 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.google.accompanist.flowlayout.FlowRow
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
@@ -99,7 +98,7 @@ import kotlinx.coroutines.launch
fun VideoScreen(
videoFeedView: NostrVideoFeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
scrollToTop: Boolean = false
) {
val lifeCycleOwner = LocalLifecycleOwner.current
@@ -142,7 +141,7 @@ fun VideoScreen(
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
FeedView(videoFeedView, accountViewModel, navController, ScrollStateKeys.VIDEO_SCREEN, scrollToTop)
FeedView(videoFeedView, accountViewModel, nav, ScrollStateKeys.VIDEO_SCREEN, scrollToTop)
}
}
}
@@ -151,7 +150,7 @@ fun VideoScreen(
fun FeedView(
videoFeedView: NostrVideoFeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
scrollStateKey: String? = null,
scrollToTop: Boolean = false
) {
@@ -176,7 +175,7 @@ fun FeedView(
SlidingCarousel(
state.feed,
accountViewModel,
navController,
nav,
scrollStateKey,
scrollToTop
)
@@ -196,7 +195,7 @@ fun FeedView(
fun SlidingCarousel(
feed: MutableState<List<Note>>,
accountViewModel: AccountViewModel,
navController: NavController,
nav: (String) -> Unit,
scrollStateKey: String? = null,
scrollToTop: Boolean = false
) {
@@ -222,7 +221,7 @@ fun SlidingCarousel(
}
) { index ->
feed.value.getOrNull(index)?.let { note ->
RenderVideoOrPictureNote(note, accountViewModel, navController)
RenderVideoOrPictureNote(note, accountViewModel, nav)
}
}
}
@@ -231,7 +230,7 @@ fun SlidingCarousel(
private fun RenderVideoOrPictureNote(
note: Note,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
val noteEvent = note.event
@@ -255,7 +254,7 @@ private fun RenderVideoOrPictureNote(
Column(Modifier.weight(1f)) {
Row(Modifier.padding(10.dp), verticalAlignment = Alignment.Bottom) {
Column(Modifier.size(55.dp), verticalArrangement = Arrangement.Center) {
NoteAuthorPicture(note, navController, loggedIn, 55.dp)
NoteAuthorPicture(note, nav, loggedIn, 55.dp)
}
Column(
@@ -299,7 +298,7 @@ private fun RenderVideoOrPictureNote(
verticalArrangement = Arrangement.Center
) {
Row(horizontalArrangement = Arrangement.Center) {
ReactionsColumn(note, accountViewModel, navController)
ReactionsColumn(note, accountViewModel, nav)
}
}
}
@@ -342,7 +341,7 @@ private fun RelayBadges(baseNote: Note) {
}
@Composable
fun ReactionsColumn(baseNote: Note, accountViewModel: AccountViewModel, navController: NavController) {
fun ReactionsColumn(baseNote: Note, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -355,11 +354,11 @@ fun ReactionsColumn(baseNote: Note, accountViewModel: AccountViewModel, navContr
}
if (wantsToReplyTo != null) {
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, account, accountViewModel, navController)
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, account, accountViewModel, nav)
}
if (wantsToQuote != null) {
NewPostView({ wantsToQuote = null }, null, wantsToQuote, account, accountViewModel, navController)
NewPostView({ wantsToQuote = null }, null, wantsToQuote, account, accountViewModel, nav)
}
Spacer(modifier = Modifier.height(8.dp))
@@ -380,7 +379,7 @@ fun ReactionsColumn(baseNote: Note, accountViewModel: AccountViewModel, navContr
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun NewImageButton(accountViewModel: AccountViewModel, navController: NavController) {
fun NewImageButton(accountViewModel: AccountViewModel, nav: (String) -> Unit) {
var wantsToPost by remember {
mutableStateOf(false)
}
@@ -397,15 +396,7 @@ fun NewImageButton(accountViewModel: AccountViewModel, navController: NavControl
// awaits an refresh on the list
delay(250)
val route = Route.Video.route.replace("{scrollToTop}", "true")
navController.navigate(route) {
navController.graph.startDestinationRoute?.let { start ->
popUpTo(start) { inclusive = false }
restoreState = true
}
launchSingleTop = true
restoreState = true
}
nav(route)
}
}
@@ -445,7 +436,7 @@ fun NewImageButton(accountViewModel: AccountViewModel, navController: NavControl
onClose = { pickedURI = null },
postViewModel = postViewModel,
accountViewModel = accountViewModel,
navController = navController
nav = nav
)
}
@@ -30,7 +30,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.core.os.ConfigurationCompat
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService
import com.vitorpamplona.amethyst.service.lang.ResultOrError
@@ -47,7 +46,7 @@ fun TranslatableRichTextViewer(
tags: List<List<String>>?,
backgroundColor: Color,
accountViewModel: AccountViewModel,
navController: NavController
nav: (String) -> Unit
) {
var translatedTextState by remember {
mutableStateOf(ResultOrError(content, null, null, null))
@@ -93,7 +92,7 @@ fun TranslatableRichTextViewer(
tags,
backgroundColor,
accountViewModel,
navController
nav
)
val target = translatedTextState.targetLang