diff --git a/app/build.gradle b/app/build.gradle index 8ba1e9907..7b4be8263 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/fdroid/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt b/app/src/fdroid/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt index 5f4659d44..a8cb1e8d9 100644 --- a/app/src/fdroid/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt +++ b/app/src/fdroid/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt @@ -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>?, backgroundColor: Color, accountViewModel: AccountViewModel, - navController: NavController + nav: (String) -> Unit ) = ExpandableRichTextViewer( content, canPreview, @@ -22,5 +21,5 @@ fun TranslatableRichTextViewer( tags, backgroundColor, accountViewModel, - navController + nav ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/NotificationCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/NotificationCache.kt index 2bb193fba..40e815cfe 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/NotificationCache.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/NotificationCache.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch object NotificationCache { + // TODO: This must be account-based val lastReadByRoute = mutableMapOf() fun markAsRead(route: String, timestampInSecs: Long) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 89c2e96a8..0bc3449ee 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -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 = DefaultChannels, @@ -1166,4 +1169,5 @@ class AccountLiveData(private val account: Account) : LiveData(Acc } } +@Immutable class AccountState(val account: Account) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt index b012aabc2..a850c01b6 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Note.kt @@ -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(note)) { } } +@Immutable class NoteState(val note: Note) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt index afc446004..3992607da 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/User.kt @@ -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(user)) { } } +@Immutable class UserState(val user: User) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/relays/Relay.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/relays/Relay.kt index b4e0a9341..41fe2823b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/relays/Relay.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/relays/Relay.kt @@ -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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt index eda028bf0..08ff4090e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/JoinUserOrChannelView.kt @@ -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) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt index cd24d89d5..efd74fb55 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewMediaView.kt @@ -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 diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index b96e0c61c..ea205a851 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/ChannelFabColumn.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/ChannelFabColumn.kt index 7a4d3dad8..84e292087 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/ChannelFabColumn.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/ChannelFabColumn.kt @@ -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() { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/NewNoteButton.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/NewNoteButton.kt index 0fe83a465..d03c1d01c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/NewNoteButton.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/buttons/NewNoteButton.kt @@ -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( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/BundledUpdate.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/BundledUpdate.kt index 2a1036fb9..9b5f60f9f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/BundledUpdate.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/BundledUpdate.kt @@ -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 } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableNoteTag.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableNoteTag.kt index 8962fc219..cd48515fa 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableNoteTag.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableNoteTag.kt @@ -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) ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt index acbc4b65f..32bb3c997 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableRoute.kt @@ -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(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(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(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(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 { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUserTag.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUserTag.kt index c56c654cc..8b8ea0ff4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUserTag.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ClickableUserTag.kt @@ -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) ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt index 8d622093f..b0f948fb3 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt @@ -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>?, 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) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt index d745aeecf..552c1fd0b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RichTextViewer.kt @@ -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>?, 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, @@ -122,10 +122,10 @@ private fun RenderRegular( canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, - navController: NavController + nav: (String) -> Unit ) { - var processedState by remember { - mutableStateOf(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) { } @Composable -private fun RenderContentAsMarkdown(content: String, backgroundColor: Color, tags: List>?, navController: NavController) { +private fun RenderContentAsMarkdown(content: String, backgroundColor: Color, tags: List>?, 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(null) } var baseNotePair by remember { mutableStateOf?>(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?>(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>, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, navController: NavController) { +fun TagLink(word: String, tags: List>, canPreview: Boolean, backgroundColor: Color, accountViewModel: AccountViewModel, nav: (String) -> Unit) { var baseUserPair by remember { mutableStateOf?>(null) } var baseNotePair by remember { mutableStateOf?>(null) } @@ -753,7 +755,7 @@ fun TagLink(word: String, tags: List>, canPreview: Boolean, backgro suffix = "${it.second} ", tags = userTags, route = route, - navController = navController + nav = nav ) } @@ -773,13 +775,13 @@ fun TagLink(word: String, tags: List>, 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} ") } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RobohashAsyncImage.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RobohashAsyncImage.kt index c387bfa5d..272f77c1f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RobohashAsyncImage.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/components/RobohashAsyncImage.kt @@ -132,6 +132,7 @@ fun RobohashAsyncImageProxy( filterQuality: FilterQuality = DrawScope.DefaultFilterQuality ) { val proxy = remember(model) { model.proxyUrl() } + if (proxy == null) { RobohashAsyncImage( robot = robot, diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt index f6c9c8823..8dd993fb0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt @@ -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(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() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt index d1005265b..492f05648 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt @@ -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 } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt index cecad69ec..2c98f68e1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppTopBar.kt @@ -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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt index d5f483769..ac088468b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt @@ -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 } ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt index a123dc0c4..ee631624c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt @@ -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 +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BadgeCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BadgeCompose.kt index 066777618..6623105b0 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BadgeCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BadgeCompose.kt @@ -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 ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BlankNote.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BlankNote.kt index a333a2b6c..a9a95fe6b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BlankNote.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BlankNote.kt @@ -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, loggedIn: User, modifier: Modifier = Modifier, isQuote: Boolean = false, navController: NavController, onClick: () -> Unit) { +fun HiddenNote(reports: Set, 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, loggedIn: User, modifier: Modifier = Modifier reports.forEach { NoteAuthorPicture( baseNote = it, - navController = navController, + nav = nav, userAccount = loggedIn, size = 35.dp ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BoostSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BoostSetCompose.kt index 10b61d0d6..4e437ea87 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BoostSetCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/BoostSetCompose.kt @@ -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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt index b4711e887..549617784 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt @@ -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}") } ) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt index e799500d1..f2f89b0f9 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt @@ -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 ) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/LikeSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/LikeSetCompose.kt index eb22f8631..ee5a90b93 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/LikeSetCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/LikeSetCompose.kt @@ -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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt index c893e0439..1e1b8c0a3 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MessageSetCompose.kt @@ -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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt index 49bd4cffc..339afcf93 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/MultiSetCompose.kt @@ -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, - navController: NavController, + likeEvents: ImmutableList, + 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, - navController: NavController, + zapEvents: ImmutableMap, + 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, - navController: NavController, + boostEvents: ImmutableList, + 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, - navController: NavController, + authorNotes: ImmutableMap, + 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, 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, - 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, - navController: NavController, + authorNotes: ImmutableList, + 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) + } } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt index 7c23d1623..540985d0e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt @@ -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(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(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, 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}") } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt index 12342c292..596b9f228 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/PollNote.kt @@ -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 ) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt index f4fbf8f41..3c0fb6278 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReactionsRow.kt @@ -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)) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt index 2d7ab31a4..19512a828 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ReplyInformation.kt @@ -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?, mentions: List, account: Account, navController: NavController) { +fun ReplyInformation(replyTo: List?, mentions: List, account: Account, nav: (String) -> Unit) { var dupMentions by remember { mutableStateOf?>(null) } LaunchedEffect(Unit) { @@ -34,7 +33,7 @@ fun ReplyInformation(replyTo: List?, mentions: List, 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?, dupMentions: List?, account: Ac } @Composable -fun ReplyInformationChannel(replyTo: List?, mentions: List, channel: Channel, account: Account, navController: NavController) { +fun ReplyInformationChannel(replyTo: List?, mentions: List, channel: Channel, account: Account, nav: (String) -> Unit) { var sortedMentions by remember { mutableStateOf?>(null) } LaunchedEffect(Unit) { @@ -134,26 +133,26 @@ fun ReplyInformationChannel(replyTo: List?, mentions: List, 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?, mentions: List?, channel: Channel, navController: NavController) { +fun ReplyInformationChannel(replyTo: List?, mentions: List?, 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}") } ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt index 1164d9067..5c366380f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt @@ -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) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserReactionsRow.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserReactionsRow.kt index b6d243aa5..7ed0e9a09 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserReactionsRow.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UserReactionsRow.kt @@ -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 diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt index aa881b1f0..de185a436 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt @@ -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, accountViewModel: AccountViewModel, navController: NavController) { +fun ZapNoteCompose(baseNote: Pair, accountViewModel: AccountViewModel, nav: (String) -> Unit) { val accountState by accountViewModel.accountLiveData.observeAsState() val account = accountState?.account ?: return @@ -60,7 +59,7 @@ fun ZapNoteCompose(baseNote: Pair, 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, 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) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapSetCompose.kt index b99f3007c..a67a18f5a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapSetCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapSetCompose.kt @@ -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) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapUserSetCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapUserSetCompose.kt index 70aa994f7..5958fdb91 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapUserSetCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapUserSetCompose.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt index f96914f78..f3c923a68 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt index df080b9f6..31de49a55 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedState.kt @@ -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) : Card() { } @Immutable -class ZapUserSetCard(val user: User, val zapEvents: Map) : Card() { +class ZapUserSetCard(val user: User, val zapEvents: ImmutableMap) : 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) : Card() { } @Immutable -class MultiSetCard(val note: Note, val boostEvents: List, val likeEvents: List, val zapEvents: Map) : Card() { +class MultiSetCard(val note: Note, val boostEvents: ImmutableList, val likeEvents: ImmutableList, val zapEvents: ImmutableMap) : 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>) : CardFeedState() + class Loaded(val feed: MutableState>) : CardFeedState() object Empty : CardFeedState() class FeedError(val errorMessage: String) : CardFeedState() } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt index 6b1d28edc..341f93f91 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedView.kt @@ -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()}") } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt index 40ed18d80..5f7567338 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt @@ -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) : 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) : 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) : ViewModel() { val userZaps = zapsPerUser.map { ZapUserSetCard( it.key, - it.value + it.value.toImmutableMap() ) } @@ -165,7 +179,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter) : ViewModel() { return (multiCards + textNoteCards + userZaps).sortedWith(compareBy({ it.createdAt() }, { it.id() })).reversed() } - private fun updateFeed(notes: List) { + private fun updateFeed(notes: ImmutableList) { val scope = CoroutineScope(Job() + Dispatchers.Main) scope.launch { val currentState = _feedContent.value @@ -173,7 +187,6 @@ open class CardFeedViewModel(val localFilter: FeedFilter) : 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) : 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) : ViewModel() { } private val bundlerInsert = BundledInsert>(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) : ViewModel() { super.onCleared() } } + +fun equalImmutableLists(list1: ImmutableList, list2: ImmutableList): Boolean { + if (list1.size != list2.size) return false + for (i in 0 until list1.size) { + if (list1[i] !== list2[i]) { + return false + } + } + return true +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomFeedView.kt index 1f67a61f3..ff929707e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomFeedView.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt index 60905dbc3..736976132 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ChatroomListFeedView.kt @@ -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 ) { 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 ) { val listState = rememberLazyListState() @@ -139,7 +138,7 @@ private fun FeedLoaded( ChatroomCompose( item, accountViewModel = accountViewModel, - navController = navController + nav = nav ) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt index e5faa3fdc..72a7b3194 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedView.kt @@ -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 ) } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LnZapFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LnZapFeedView.kt index f6811611b..e32473ad1 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LnZapFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/LnZapFeedView.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt index cc1d47a35..86f85c37f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/ThreadFeedView.kt @@ -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), diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt index d2f8078cd..4c3946e6d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedView.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 05e5c7fa6..90dfbb5d4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -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 = account.live.map { it } val accountLanguagesLiveData: LiveData = 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 create(modelClass: Class): AccountViewModel { + return AccountViewModel(account) as AccountViewModel + } + } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/BookmarkListScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/BookmarkListScreen.kt index b9e79bef6..c81b6ea74 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/BookmarkListScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/BookmarkListScreen.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt index 1af7f02c3..2155f2e5d 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt @@ -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 diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt index 8c7c26e02..880006ecd 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt @@ -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 ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt index cf75e066a..c853709de 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt @@ -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)) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HashtagScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HashtagScreen.kt index b2cd429e8..4d8489073 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HashtagScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HashtagScreen.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt index 243e57289..fa8a42c76 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt index 713cafd32..da87f72ec 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt @@ -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 diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt index e230e9d42..035034165 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt index 79843b121..61ae8598e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NotificationScreen.kt @@ -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 } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt index 485892a66..1fb9482eb 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ProfileScreen.kt @@ -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(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( diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt index 320e913c0..2bdce2350 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/SearchScreen.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ThreadScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ThreadScreen.kt index 2d5d6aa53..524711e09 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ThreadScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ThreadScreen.kt @@ -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) } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt index 7dc3d86dc..e6b9185dc 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt @@ -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>, 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 ) } diff --git a/app/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt b/app/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt index 8e55bfcca..4825ac45b 100644 --- a/app/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt +++ b/app/src/play/java/com/vitorpamplona/amethyst/ui/components/TranslatableRichTextViewer.kt @@ -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>?, 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