mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-04-08 20:08:06 +02:00
- Inverts Layouts to place Navigation on top of Scaffold and allow custom scaffolds per route.
- Finishes Edge to Edge transition - Fixes moving buttons on the full screen dialog for images and videos. - Restructures screens into their own packages. - Restructures navigation functions as a single object - Fixes padding of the backup screen - Refactors all TopBars to use default material 3 ones. - Refactors navigation to improve clarity - Removes unnecessary observers from the transition in the bottom nav layouts.
This commit is contained in:
parent
0217fd3eb3
commit
ead588314e
@ -55,10 +55,10 @@ import com.halilibo.richtext.ui.resolveDefaults
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.notifications.PushDistributorHandler
|
||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SettingsRow
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.checkifItNeedsToRequestNotificationPermission
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsRow
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
@ -24,6 +24,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.events.ImmutableListOfLists
|
||||
|
||||
@ -38,7 +39,7 @@ fun TranslatableRichTextViewer(
|
||||
id: String,
|
||||
callbackUri: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) = ExpandableRichTextViewer(
|
||||
content,
|
||||
canPreview,
|
||||
|
180
amethyst/src/main/java/com/vitorpamplona/amethyst/DebugUtils.kt
Normal file
180
amethyst/src/main/java/com/vitorpamplona/amethyst/DebugUtils.kt
Normal file
@ -0,0 +1,180 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.os.Debug
|
||||
import android.util.Log
|
||||
import androidx.core.content.getSystemService
|
||||
import coil.Coil
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrCommunityDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrDiscoveryDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrGeohashDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrHashtagDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrSingleChannelDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
||||
import com.vitorpamplona.ammolite.relays.Client
|
||||
import com.vitorpamplona.ammolite.relays.RelayPool
|
||||
|
||||
fun debugState(context: Context) {
|
||||
Client
|
||||
.allSubscriptions()
|
||||
.forEach { Log.d("STATE DUMP", "${it.key} ${it.value.joinToString { it.filter.toDebugJson() }}") }
|
||||
|
||||
NostrAccountDataSource.printCounter()
|
||||
NostrChannelDataSource.printCounter()
|
||||
NostrChatroomDataSource.printCounter()
|
||||
NostrChatroomListDataSource.printCounter()
|
||||
NostrCommunityDataSource.printCounter()
|
||||
NostrDiscoveryDataSource.printCounter()
|
||||
NostrHashtagDataSource.printCounter()
|
||||
NostrGeohashDataSource.printCounter()
|
||||
NostrHomeDataSource.printCounter()
|
||||
NostrSearchEventOrUserDataSource.printCounter()
|
||||
NostrSingleChannelDataSource.printCounter()
|
||||
NostrSingleEventDataSource.printCounter()
|
||||
NostrSingleUserDataSource.printCounter()
|
||||
NostrThreadDataSource.printCounter()
|
||||
NostrUserProfileDataSource.printCounter()
|
||||
NostrVideoDataSource.printCounter()
|
||||
|
||||
val totalMemoryMb = Runtime.getRuntime().totalMemory() / (1024 * 1024)
|
||||
val freeMemoryMb = Runtime.getRuntime().freeMemory() / (1024 * 1024)
|
||||
val maxMemoryMb = Runtime.getRuntime().maxMemory() / (1024 * 1024)
|
||||
|
||||
val jvmHeapAllocatedMb = totalMemoryMb - freeMemoryMb
|
||||
|
||||
Log.d("STATE DUMP", "Total Heap Allocated: " + jvmHeapAllocatedMb + "/" + maxMemoryMb + " MB")
|
||||
|
||||
val nativeHeap = Debug.getNativeHeapAllocatedSize() / (1024 * 1024)
|
||||
val maxNative = Debug.getNativeHeapSize() / (1024 * 1024)
|
||||
|
||||
Log.d("STATE DUMP", "Total Native Heap Allocated: " + nativeHeap + "/" + maxNative + " MB")
|
||||
|
||||
val activityManager: ActivityManager? = context.getSystemService()
|
||||
if (activityManager != null) {
|
||||
val isLargeHeap = (context.applicationInfo.flags and ApplicationInfo.FLAG_LARGE_HEAP) != 0
|
||||
val memClass = if (isLargeHeap) activityManager.largeMemoryClass else activityManager.memoryClass
|
||||
|
||||
Log.d("STATE DUMP", "Memory Class " + memClass + " MB (largeHeap $isLargeHeap)")
|
||||
}
|
||||
|
||||
Log.d("STATE DUMP", "Connected Relays: " + RelayPool.connectedRelays())
|
||||
|
||||
val imageLoader = Coil.imageLoader(context)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Image Disk Cache ${(imageLoader.diskCache?.size ?: 0) / (1024 * 1024)}/${(imageLoader.diskCache?.maxSize ?: 0) / (1024 * 1024)} MB",
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Image Memory Cache ${(imageLoader.memoryCache?.size ?: 0) / (1024 * 1024)}/${(imageLoader.memoryCache?.maxSize ?: 0) / (1024 * 1024)} MB",
|
||||
)
|
||||
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Notes: " +
|
||||
LocalCache.notes.filter { _, it -> it.liveSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.notes.filter { _, it -> it.flowSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.notes.filter { _, it -> it.event != null }.size +
|
||||
" / " +
|
||||
LocalCache.notes.size(),
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Addressables: " +
|
||||
LocalCache.addressables.filter { _, it -> it.liveSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.addressables.filter { _, it -> it.flowSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.addressables.filter { _, it -> it.event != null }.size +
|
||||
" / " +
|
||||
LocalCache.addressables.size(),
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Users: " +
|
||||
LocalCache.users.filter { _, it -> it.liveSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.users.filter { _, it -> it.flowSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.users.filter { _, it -> it.latestMetadata != null }.size +
|
||||
" / " +
|
||||
LocalCache.users.size(),
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Deletion Events: " +
|
||||
LocalCache.deletionIndex.size(),
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Observable Events: " +
|
||||
LocalCache.observablesByKindAndETag.size +
|
||||
" / " +
|
||||
LocalCache.observablesByKindAndAuthor.size,
|
||||
)
|
||||
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Spam: " +
|
||||
LocalCache.antiSpam.spamMessages.size() + " / " + LocalCache.antiSpam.recentMessages.size(),
|
||||
)
|
||||
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Memory used by Events: " +
|
||||
LocalCache.notes.sumOfLong { _, note -> note.event?.countMemory() ?: 0L } / (1024 * 1024) +
|
||||
" MB",
|
||||
)
|
||||
|
||||
val qttNotes = LocalCache.notes.countByGroup { _, it -> it.event?.kind() }
|
||||
val qttAddressables = LocalCache.addressables.countByGroup { _, it -> it.event?.kind() }
|
||||
|
||||
val bytesNotes =
|
||||
LocalCache.notes
|
||||
.sumByGroup(groupMap = { _, it -> it.event?.kind() }, sumOf = { _, it -> it.event?.countMemory() ?: 0L })
|
||||
val bytesAddressables =
|
||||
LocalCache.addressables
|
||||
.sumByGroup(groupMap = { _, it -> it.event?.kind() }, sumOf = { _, it -> it.event?.countMemory() ?: 0L })
|
||||
|
||||
qttNotes.toList().sortedByDescending { bytesNotes.get(it.first) }.forEach { (kind, qtt) ->
|
||||
Log.d("STATE DUMP", "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesNotes.get(kind)?.div((1024 * 1024))}MB ")
|
||||
}
|
||||
qttAddressables.toList().sortedByDescending { bytesNotes.get(it.first) }.forEach { (kind, qtt) ->
|
||||
Log.d("STATE DUMP", "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesAddressables.get(kind)?.div((1024 * 1024))}MB ")
|
||||
}
|
||||
}
|
@ -47,21 +47,20 @@ import com.vitorpamplona.amethyst.commons.richtext.HashTagSegment
|
||||
import com.vitorpamplona.amethyst.commons.richtext.RegularTextSegment
|
||||
import com.vitorpamplona.amethyst.ui.components.HashTag
|
||||
import com.vitorpamplona.amethyst.ui.components.RenderRegular
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
|
||||
import com.vitorpamplona.quartz.events.EmptyTagList
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun RenderHashTagIcons() {
|
||||
val nav: (String) -> Unit = {}
|
||||
|
||||
fun RenderHashTagIconsPreview() {
|
||||
ThemeComparisonColumn {
|
||||
RenderRegular(
|
||||
"Testing rendering of hashtags: #Bitcoin, #nostr, #lightning, #zap, #amethyst, #cashu, #plebs, #coffee, #skullofsatoshi, #grownostr, #footstr, #tunestr, #weed, #mate",
|
||||
EmptyTagList,
|
||||
) { word, state ->
|
||||
when (word) {
|
||||
is HashTagSegment -> HashTag(word, nav)
|
||||
is HashTagSegment -> HashTag(word, EmptyNav)
|
||||
is RegularTextSegment -> Text(word.segmentText)
|
||||
}
|
||||
}
|
||||
|
@ -37,13 +37,13 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.LocalPreferences
|
||||
import com.vitorpamplona.amethyst.debugState
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService
|
||||
import com.vitorpamplona.amethyst.service.notifications.PushNotificationUtils
|
||||
import com.vitorpamplona.amethyst.ui.components.DEFAULT_MUTED_SETTING
|
||||
import com.vitorpamplona.amethyst.ui.components.keepPlayingMutex
|
||||
import com.vitorpamplona.amethyst.ui.navigation.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.debugState
|
||||
import com.vitorpamplona.ammolite.service.HttpClientManager
|
||||
import com.vitorpamplona.quartz.encoders.Nip19Bech32
|
||||
import com.vitorpamplona.quartz.encoders.Nip47WalletConnect
|
||||
|
@ -94,9 +94,10 @@ import com.vitorpamplona.amethyst.ui.components.BechLink
|
||||
import com.vitorpamplona.amethyst.ui.components.InvoiceRequest
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadUrlPreview
|
||||
import com.vitorpamplona.amethyst.ui.components.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.UserLine
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.UserLine
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
@ -119,7 +120,7 @@ fun EditPostView(
|
||||
edit: Note,
|
||||
versionLookingAt: Note?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val postViewModel: EditPostViewModel = viewModel()
|
||||
postViewModel.prepare(edit, versionLookingAt, accountViewModel)
|
||||
|
@ -75,12 +75,13 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.SearchIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchBarViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChannelName
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.SearchBarViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
@ -102,7 +103,7 @@ import kotlinx.coroutines.withContext
|
||||
fun JoinUserOrChannelView(
|
||||
onClose: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val searchBarViewModel: SearchBarViewModel =
|
||||
viewModel(
|
||||
@ -126,7 +127,7 @@ fun JoinUserOrChannelView(
|
||||
searchBarViewModel: SearchBarViewModel,
|
||||
onClose: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = {
|
||||
@ -183,7 +184,7 @@ fun JoinUserOrChannelView(
|
||||
private fun RenderSearch(
|
||||
searchBarViewModel: SearchBarViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
@ -324,7 +325,7 @@ private fun RenderSearchResults(
|
||||
searchBarViewModel: SearchBarViewModel,
|
||||
listState: LazyListState,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
if (searchBarViewModel.isSearching) {
|
||||
val users by searchBarViewModel.searchResultsUsers.collectAsStateWithLifecycle()
|
||||
@ -347,7 +348,7 @@ private fun RenderSearchResults(
|
||||
key = { _, item -> "u" + item.pubkeyHex },
|
||||
) { _, item ->
|
||||
UserComposeForChat(item, accountViewModel) {
|
||||
accountViewModel.createChatRoomFor(item) { nav("Room/$it") }
|
||||
accountViewModel.createChatRoomFor(item) { nav.nav("Room/$it") }
|
||||
|
||||
searchBarViewModel.clear()
|
||||
}
|
||||
@ -366,7 +367,7 @@ private fun RenderSearchResults(
|
||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||
) {
|
||||
nav("Channel/${item.idHex}")
|
||||
nav.nav("Channel/${item.idHex}")
|
||||
searchBarViewModel.clear()
|
||||
}
|
||||
|
||||
|
@ -67,6 +67,7 @@ import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.Nip96MediaServers
|
||||
import com.vitorpamplona.amethyst.ui.components.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
@ -84,7 +85,7 @@ fun NewMediaView(
|
||||
onClose: () -> Unit,
|
||||
postViewModel: NewMediaModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val account = accountViewModel.account
|
||||
val resolver = LocalContext.current.contentResolver
|
||||
|
@ -135,6 +135,7 @@ import com.vitorpamplona.amethyst.ui.components.InvoiceRequest
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadUrlPreview
|
||||
import com.vitorpamplona.amethyst.ui.components.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.components.ZapRaiserRequest
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.CancelIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.CloseIcon
|
||||
@ -193,7 +194,7 @@ fun NewPostView(
|
||||
draft: Note? = null,
|
||||
enableMessageInterface: Boolean = false,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val postViewModel: NewPostViewModel = viewModel()
|
||||
postViewModel.wantsDirectMessage = enableMessageInterface
|
||||
|
@ -40,6 +40,8 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size16dp
|
||||
@ -52,7 +54,7 @@ fun NotifyRequestDialog(
|
||||
textContent: String,
|
||||
buttonColors: ButtonColors = ButtonDefaults.buttonColors(),
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
@ -72,7 +74,7 @@ fun NotifyRequestDialog(
|
||||
id = textContent,
|
||||
callbackUri = null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
nav = rememberExtendedNav(nav, onDismiss),
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
|
@ -48,6 +48,7 @@ import androidx.compose.ui.window.DialogProperties
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.RelayInformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
@ -74,7 +75,7 @@ fun RelaySelectionDialog(
|
||||
onClose: () -> Unit,
|
||||
onPost: (list: ImmutableList<RelaySetupInfo>) -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
|
@ -58,6 +58,7 @@ import com.vitorpamplona.amethyst.service.Nip96MediaServers
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.SettingsCategoryWithButton
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertPadding
|
||||
@ -70,7 +71,7 @@ import com.vitorpamplona.amethyst.ui.theme.grayText
|
||||
fun MediaServersListView(
|
||||
onClose: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val mediaServersViewModel: MediaServersViewModel = viewModel()
|
||||
val mediaServersState by mediaServersViewModel.fileServers.collectAsStateWithLifecycle()
|
||||
|
@ -44,6 +44,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
@ -55,7 +56,7 @@ import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||
fun AddDMRelayListDialog(
|
||||
onClose: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val postViewModel: DMRelayListViewModel = viewModel()
|
||||
|
||||
|
@ -44,6 +44,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||
@ -55,7 +56,7 @@ import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||
fun AddSearchRelayListDialog(
|
||||
onClose: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val postViewModel: SearchRelayListViewModel = viewModel()
|
||||
|
||||
|
@ -51,6 +51,8 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
@ -61,13 +63,23 @@ import com.vitorpamplona.amethyst.ui.theme.grayText
|
||||
import com.vitorpamplona.ammolite.relays.Constants
|
||||
import com.vitorpamplona.ammolite.relays.RelayStat
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AllRelayListView(
|
||||
onClose: () -> Unit,
|
||||
relayToAdd: String = "",
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
MappedAllRelayListView(onClose, relayToAdd, accountViewModel, rememberExtendedNav(nav, onClose))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MappedAllRelayListView(
|
||||
onClose: () -> Unit,
|
||||
relayToAdd: String = "",
|
||||
accountViewModel: AccountViewModel,
|
||||
newNav: INav,
|
||||
) {
|
||||
val kind3ViewModel: Kind3RelayListViewModel = viewModel()
|
||||
val kind3FeedState by kind3ViewModel.relays.collectAsStateWithLifecycle()
|
||||
@ -179,7 +191,7 @@ fun AllRelayListView(
|
||||
Modifier.padding(bottom = 8.dp),
|
||||
)
|
||||
}
|
||||
renderNip65HomeItems(homeFeedState, nip65ViewModel, accountViewModel, onClose, nav)
|
||||
renderNip65HomeItems(homeFeedState, nip65ViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
@ -187,7 +199,7 @@ fun AllRelayListView(
|
||||
stringRes(R.string.public_notif_section_explainer),
|
||||
)
|
||||
}
|
||||
renderNip65NotifItems(notifFeedState, nip65ViewModel, accountViewModel, onClose, nav)
|
||||
renderNip65NotifItems(notifFeedState, nip65ViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
@ -195,7 +207,7 @@ fun AllRelayListView(
|
||||
stringRes(R.string.private_inbox_section_explainer),
|
||||
)
|
||||
}
|
||||
renderDMItems(dmFeedState, dmViewModel, accountViewModel, onClose, nav)
|
||||
renderDMItems(dmFeedState, dmViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
@ -203,7 +215,7 @@ fun AllRelayListView(
|
||||
stringRes(R.string.private_outbox_section_explainer),
|
||||
)
|
||||
}
|
||||
renderPrivateOutboxItems(privateOutboxFeedState, privateOutboxViewModel, accountViewModel, onClose, nav)
|
||||
renderPrivateOutboxItems(privateOutboxFeedState, privateOutboxViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
@ -214,7 +226,7 @@ fun AllRelayListView(
|
||||
},
|
||||
)
|
||||
}
|
||||
renderSearchItems(searchFeedState, searchViewModel, accountViewModel, onClose, nav)
|
||||
renderSearchItems(searchFeedState, searchViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
@ -222,7 +234,7 @@ fun AllRelayListView(
|
||||
stringRes(R.string.local_section_explainer),
|
||||
)
|
||||
}
|
||||
renderLocalItems(localFeedState, localViewModel, accountViewModel, onClose, nav)
|
||||
renderLocalItems(localFeedState, localViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
@ -233,7 +245,7 @@ fun AllRelayListView(
|
||||
},
|
||||
)
|
||||
}
|
||||
renderKind3Items(kind3FeedState, kind3ViewModel, accountViewModel, onClose, nav, relayToAdd)
|
||||
renderKind3Items(kind3FeedState, kind3ViewModel, accountViewModel, newNav, relayToAdd)
|
||||
|
||||
if (kind3Proposals.isNotEmpty()) {
|
||||
item {
|
||||
@ -242,7 +254,7 @@ fun AllRelayListView(
|
||||
stringRes(R.string.kind_3_recommended_section_description),
|
||||
)
|
||||
}
|
||||
renderKind3ProposalItems(kind3Proposals, kind3ViewModel, accountViewModel, onClose, nav)
|
||||
renderKind3ProposalItems(kind3Proposals, kind3ViewModel, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.RelayInfoDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
|
||||
@ -39,7 +40,7 @@ fun BasicRelaySetupInfoDialog(
|
||||
item: BasicRelaySetupInfo,
|
||||
onDelete: (BasicRelaySetupInfo) -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var relayInfo: RelayInfoDialog? by remember { mutableStateOf(null) }
|
||||
val context = LocalContext.current
|
||||
|
@ -29,6 +29,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@ -38,15 +40,16 @@ fun DMRelayList(
|
||||
postViewModel: DMRelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val newNav = rememberExtendedNav(nav, onClose)
|
||||
val feedState by postViewModel.relays.collectAsStateWithLifecycle()
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
LazyColumn(
|
||||
contentPadding = FeedPadding,
|
||||
) {
|
||||
renderDMItems(feedState, postViewModel, accountViewModel, onClose, nav)
|
||||
renderDMItems(feedState, postViewModel, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,18 +58,15 @@ fun LazyListScope.renderDMItems(
|
||||
feedState: List<BasicRelaySetupInfo>,
|
||||
postViewModel: DMRelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
itemsIndexed(feedState, key = { _, item -> "DM" + item.url }) { index, item ->
|
||||
BasicRelaySetupInfoDialog(
|
||||
item,
|
||||
onDelete = { postViewModel.deleteRelay(item) },
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
onClose()
|
||||
nav(it)
|
||||
}
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
|
@ -71,6 +71,8 @@ import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.RelayInfoDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@ -102,12 +104,14 @@ fun Kind3RelayListView(
|
||||
postViewModel: Kind3RelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
relayToAdd: String,
|
||||
) {
|
||||
val newNav = rememberExtendedNav(nav, onClose)
|
||||
|
||||
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
|
||||
LazyColumn(contentPadding = FeedPadding) {
|
||||
renderKind3Items(feedState, postViewModel, accountViewModel, onClose, nav, relayToAdd)
|
||||
renderKind3Items(feedState, postViewModel, accountViewModel, newNav, relayToAdd)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,8 +120,7 @@ fun LazyListScope.renderKind3Items(
|
||||
feedState: List<Kind3BasicRelaySetupInfo>,
|
||||
postViewModel: Kind3RelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
relayToAdd: String,
|
||||
) {
|
||||
itemsIndexed(feedState, key = { _, item -> "kind3" + item.url }) { index, item ->
|
||||
@ -132,10 +135,8 @@ fun LazyListScope.renderKind3Items(
|
||||
onToggleSearch = { postViewModel.toggleSearch(it) },
|
||||
onDelete = { postViewModel.deleteRelay(it) },
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
onClose()
|
||||
nav(it)
|
||||
}
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
@ -148,8 +149,7 @@ fun LazyListScope.renderKind3ProposalItems(
|
||||
feedState: List<Kind3RelayProposalSetupInfo>,
|
||||
postViewModel: Kind3RelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
itemsIndexed(feedState, key = { _, item -> "kind3proposal" + item.url }) { index, item ->
|
||||
Kind3RelaySetupInfoProposalDialog(
|
||||
@ -158,10 +158,7 @@ fun LazyListScope.renderKind3ProposalItems(
|
||||
postViewModel.addRelay(item)
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
nav = {
|
||||
onClose()
|
||||
nav(it)
|
||||
},
|
||||
nav = nav,
|
||||
)
|
||||
HorizontalDivider(
|
||||
thickness = DividerThickness,
|
||||
@ -214,7 +211,7 @@ fun LoadRelayInfo(
|
||||
onToggleSearch: (Kind3BasicRelaySetupInfo) -> Unit,
|
||||
onDelete: (Kind3BasicRelaySetupInfo) -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var relayInfo: RelayInfoDialog? by remember { mutableStateOf(null) }
|
||||
val context = LocalContext.current
|
||||
|
@ -30,6 +30,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.RelayInfoDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
|
||||
@ -39,7 +40,7 @@ fun Kind3RelaySetupInfoProposalDialog(
|
||||
item: Kind3RelayProposalSetupInfo,
|
||||
onAdd: (Kind3RelayProposalSetupInfo) -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var relayInfo: RelayInfoDialog? by remember { mutableStateOf(null) }
|
||||
val context = LocalContext.current
|
||||
|
@ -47,6 +47,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.AddRelayButton
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
@ -72,7 +74,7 @@ fun Kind3RelaySetupInfoProposalRow(
|
||||
onAdd: () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
@ -138,7 +140,6 @@ fun Kind3RelaySetupInfoProposalRow(
|
||||
@Preview
|
||||
@Composable
|
||||
fun UsedByPreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
ThemeComparisonColumn {
|
||||
UsedBy(
|
||||
item =
|
||||
@ -151,9 +152,9 @@ fun UsedByPreview() {
|
||||
paidRelay = false,
|
||||
users = listOf("User1", "User2", "User3", "User4"),
|
||||
),
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
}
|
||||
accountViewModel = mockAccountViewModel(),
|
||||
nav = EmptyNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +163,7 @@ fun UsedByPreview() {
|
||||
fun UsedBy(
|
||||
item: Kind3RelayProposalSetupInfo,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
FlowRow(verticalArrangement = Arrangement.Center) {
|
||||
item.users.getOrNull(0)?.let {
|
||||
|
@ -29,6 +29,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@ -38,15 +40,16 @@ fun LocalRelayList(
|
||||
postViewModel: LocalRelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val newNav = rememberExtendedNav(nav, onClose)
|
||||
val feedState by postViewModel.relays.collectAsStateWithLifecycle()
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
LazyColumn(
|
||||
contentPadding = FeedPadding,
|
||||
) {
|
||||
renderLocalItems(feedState, postViewModel, accountViewModel, onClose, nav)
|
||||
renderLocalItems(feedState, postViewModel, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,18 +58,15 @@ fun LazyListScope.renderLocalItems(
|
||||
feedState: List<BasicRelaySetupInfo>,
|
||||
postViewModel: LocalRelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
itemsIndexed(feedState, key = { _, item -> "Local" + item.url }) { index, item ->
|
||||
BasicRelaySetupInfoDialog(
|
||||
item,
|
||||
onDelete = { postViewModel.deleteRelay(item) },
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
onClose()
|
||||
nav(it)
|
||||
}
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
|
@ -29,6 +29,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@ -38,8 +40,10 @@ fun Nip65RelayList(
|
||||
postViewModel: Nip65RelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val newNav = rememberExtendedNav(nav, onClose)
|
||||
|
||||
val homeFeedState by postViewModel.homeRelays.collectAsStateWithLifecycle()
|
||||
val notifFeedState by postViewModel.notificationRelays.collectAsStateWithLifecycle()
|
||||
|
||||
@ -47,8 +51,8 @@ fun Nip65RelayList(
|
||||
LazyColumn(
|
||||
contentPadding = FeedPadding,
|
||||
) {
|
||||
renderNip65HomeItems(homeFeedState, postViewModel, accountViewModel, onClose, nav)
|
||||
renderNip65NotifItems(notifFeedState, postViewModel, accountViewModel, onClose, nav)
|
||||
renderNip65HomeItems(homeFeedState, postViewModel, accountViewModel, newNav)
|
||||
renderNip65NotifItems(notifFeedState, postViewModel, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -57,18 +61,15 @@ fun LazyListScope.renderNip65HomeItems(
|
||||
feedState: List<BasicRelaySetupInfo>,
|
||||
postViewModel: Nip65RelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
itemsIndexed(feedState, key = { _, item -> "Nip65Home" + item.url }) { index, item ->
|
||||
BasicRelaySetupInfoDialog(
|
||||
item,
|
||||
onDelete = { postViewModel.deleteHomeRelay(item) },
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
onClose()
|
||||
nav(it)
|
||||
}
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
@ -81,18 +82,15 @@ fun LazyListScope.renderNip65NotifItems(
|
||||
feedState: List<BasicRelaySetupInfo>,
|
||||
postViewModel: Nip65RelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
itemsIndexed(feedState, key = { _, item -> "Nip65Notif" + item.url }) { index, item ->
|
||||
BasicRelaySetupInfoDialog(
|
||||
item,
|
||||
onDelete = { postViewModel.deleteNotifRelay(item) },
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
onClose()
|
||||
nav(it)
|
||||
}
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
|
@ -29,6 +29,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@ -38,15 +40,16 @@ fun PrivateOutboxRelayList(
|
||||
postViewModel: PrivateOutboxRelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val newNav = rememberExtendedNav(nav, onClose)
|
||||
val feedState by postViewModel.relays.collectAsStateWithLifecycle()
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
LazyColumn(
|
||||
contentPadding = FeedPadding,
|
||||
) {
|
||||
renderPrivateOutboxItems(feedState, postViewModel, accountViewModel, onClose, nav)
|
||||
renderPrivateOutboxItems(feedState, postViewModel, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,18 +58,15 @@ fun LazyListScope.renderPrivateOutboxItems(
|
||||
feedState: List<BasicRelaySetupInfo>,
|
||||
postViewModel: PrivateOutboxRelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
itemsIndexed(feedState, key = { _, item -> "Outbox" + item.url }) { index, item ->
|
||||
BasicRelaySetupInfoDialog(
|
||||
item,
|
||||
onDelete = { postViewModel.deleteRelay(item) },
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
onClose()
|
||||
nav(it)
|
||||
}
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
|
@ -55,6 +55,8 @@ import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableEmail
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.timeAgo
|
||||
@ -79,8 +81,10 @@ fun RelayInformationDialog(
|
||||
relayBriefInfo: RelayBriefInfoCache.RelayBriefInfo,
|
||||
relayInfo: Nip11RelayInformation,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val newNav = rememberExtendedNav(nav, onClose)
|
||||
|
||||
val messages =
|
||||
remember(relayBriefInfo) {
|
||||
RelayStats
|
||||
@ -152,10 +156,7 @@ fun RelayInformationDialog(
|
||||
Section(stringRes(R.string.owner))
|
||||
|
||||
relayInfo.pubkey?.let {
|
||||
DisplayOwnerInformation(it, accountViewModel) {
|
||||
onClose()
|
||||
nav(it)
|
||||
}
|
||||
DisplayOwnerInformation(it, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
item {
|
||||
@ -300,7 +301,7 @@ fun RelayInformationDialog(
|
||||
backgroundColor = color,
|
||||
id = msg.hashCode().toString(),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
nav = newNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -353,7 +354,7 @@ private fun DisplaySoftwareInformation(relayInfo: Nip11RelayInformation) {
|
||||
private fun DisplayOwnerInformation(
|
||||
userHex: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadUser(baseUserHex = userHex, accountViewModel) {
|
||||
CrossfadeIfEnabled(it, accountViewModel = accountViewModel) {
|
||||
|
@ -29,6 +29,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@ -38,15 +40,17 @@ fun SearchRelayList(
|
||||
postViewModel: SearchRelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val feedState by postViewModel.relays.collectAsStateWithLifecycle()
|
||||
|
||||
val newNav = rememberExtendedNav(nav, onClose)
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
LazyColumn(
|
||||
contentPadding = FeedPadding,
|
||||
) {
|
||||
renderSearchItems(feedState, postViewModel, accountViewModel, onClose, nav)
|
||||
renderSearchItems(feedState, postViewModel, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -55,18 +59,15 @@ fun LazyListScope.renderSearchItems(
|
||||
feedState: List<BasicRelaySetupInfo>,
|
||||
postViewModel: SearchRelayListViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
itemsIndexed(feedState, key = { _, item -> "Search" + item.url }) { index, item ->
|
||||
BasicRelaySetupInfoDialog(
|
||||
item,
|
||||
onDelete = { postViewModel.deleteRelay(item) },
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
onClose()
|
||||
nav(it)
|
||||
}
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
|
@ -1,69 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.buttons
|
||||
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewChannelView
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
|
||||
|
||||
@Composable
|
||||
fun NewChannelButton(accountViewModel: AccountViewModel) {
|
||||
var wantsToPost by remember { mutableStateOf(false) }
|
||||
|
||||
if (wantsToPost) {
|
||||
NewChannelView({ wantsToPost = false }, accountViewModel = accountViewModel)
|
||||
}
|
||||
|
||||
OutlinedButton(
|
||||
onClick = { wantsToPost = true },
|
||||
modifier = Size55Modifier,
|
||||
shape = CircleShape,
|
||||
colors =
|
||||
ButtonDefaults.outlinedButtonColors(containerColor = MaterialTheme.colorScheme.primary),
|
||||
contentPadding = ZeroPadding,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Add,
|
||||
contentDescription = stringRes(R.string.new_channel),
|
||||
modifier = Modifier.size(26.dp),
|
||||
tint = Color.White,
|
||||
)
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -34,13 +35,13 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
fun ClickableNoteTag(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val route = routeFor(baseNote, accountViewModel.userProfile())
|
||||
|
||||
ClickableText(
|
||||
text = AnnotatedString("@${baseNote.idNote().toShortenHex()}"),
|
||||
onClick = { nav("Note/${baseNote.idHex}") },
|
||||
onClick = { nav.nav("Note/${baseNote.idHex}") },
|
||||
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary),
|
||||
)
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadChannel
|
||||
import com.vitorpamplona.amethyst.ui.note.njumpLink
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -78,7 +79,7 @@ fun ClickableRoute(
|
||||
word: String,
|
||||
nip19: Nip19Bech32.ParseReturn,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
when (val entity = nip19.entity) {
|
||||
is Nip19Bech32.NPub -> DisplayUser(entity.hex, nip19.nip19raw, nip19.additionalChars, accountViewModel, nav)
|
||||
@ -122,7 +123,7 @@ private fun LoadAndDisplayEvent(
|
||||
event: Event,
|
||||
additionalChars: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadOrCreateNote(event, accountViewModel) {
|
||||
if (it != null) {
|
||||
@ -150,7 +151,7 @@ fun DisplayEvent(
|
||||
nip19: String,
|
||||
additionalChars: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadNote(hex, accountViewModel) {
|
||||
if (it != null) {
|
||||
@ -178,7 +179,7 @@ private fun DisplayNoteLink(
|
||||
kind: Int?,
|
||||
addedCharts: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by it.live().metadata.observeAsState()
|
||||
|
||||
@ -233,7 +234,7 @@ private fun DisplayAddress(
|
||||
originalNip19: String,
|
||||
additionalChars: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var noteBase by remember(nip19) { mutableStateOf(accountViewModel.getNoteIfExists(nip19.atag)) }
|
||||
|
||||
@ -277,7 +278,7 @@ public fun DisplayUser(
|
||||
originalNip19: String,
|
||||
additionalChars: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var userBase by
|
||||
remember(userHex) {
|
||||
@ -312,7 +313,7 @@ public fun DisplayUser(
|
||||
public fun RenderUserAsClickableText(
|
||||
baseUser: User,
|
||||
additionalChars: String?,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val userState by baseUser.live().userMetadataInfo.observeAsState()
|
||||
|
||||
@ -335,7 +336,7 @@ fun CreateClickableText(
|
||||
fontWeight: FontWeight? = null,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
route: String,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
CreateClickableText(
|
||||
clickablePart,
|
||||
@ -344,7 +345,7 @@ fun CreateClickableText(
|
||||
overrideColor,
|
||||
fontWeight,
|
||||
fontSize,
|
||||
) { nav(route) }
|
||||
) { nav.nav(route) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -627,7 +628,7 @@ fun CreateClickableTextWithEmoji(
|
||||
fontWeight: FontWeight = FontWeight.Normal,
|
||||
fontSize: TextUnit = TextUnit.Unspecified,
|
||||
route: String,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
tags: ImmutableListOfLists<String>?,
|
||||
) {
|
||||
CustomEmojiChecker(
|
||||
@ -658,7 +659,7 @@ fun CreateClickableTextWithEmoji(
|
||||
suffix,
|
||||
nonClickablePartStyle,
|
||||
) {
|
||||
nav(route)
|
||||
nav.nav(route)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -29,11 +29,12 @@ import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
|
||||
@Composable
|
||||
fun ClickableUserTag(
|
||||
user: User,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val route = remember { "User/${user.pubkeyHex}" }
|
||||
|
||||
@ -44,7 +45,7 @@ fun ClickableUserTag(
|
||||
|
||||
ClickableText(
|
||||
text = userName,
|
||||
onClick = { nav(route) },
|
||||
onClick = { nav.nav(route) },
|
||||
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary),
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.actions.InformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ResourceToastMsg
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.StringToastMsg
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ThrowableToastMsg
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
|
||||
@Composable
|
||||
fun DisplayErrorMessages(accountViewModel: AccountViewModel) {
|
||||
val openDialogMsg = accountViewModel.toasts.collectAsStateWithLifecycle(null)
|
||||
|
||||
openDialogMsg.value?.let { obj ->
|
||||
when (obj) {
|
||||
is ResourceToastMsg ->
|
||||
if (obj.params != null) {
|
||||
InformationDialog(
|
||||
stringRes(obj.titleResId),
|
||||
stringRes(obj.resourceId, *obj.params),
|
||||
) {
|
||||
accountViewModel.clearToasts()
|
||||
}
|
||||
} else {
|
||||
InformationDialog(
|
||||
stringRes(obj.titleResId),
|
||||
stringRes(obj.resourceId),
|
||||
) {
|
||||
accountViewModel.clearToasts()
|
||||
}
|
||||
}
|
||||
|
||||
is StringToastMsg ->
|
||||
InformationDialog(
|
||||
obj.title,
|
||||
obj.msg,
|
||||
) {
|
||||
accountViewModel.clearToasts()
|
||||
}
|
||||
|
||||
is ThrowableToastMsg ->
|
||||
InformationDialog(
|
||||
stringRes(obj.titleResId),
|
||||
obj.msg,
|
||||
obj.throwable,
|
||||
) {
|
||||
accountViewModel.clearToasts()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.components
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.NotifyRequestDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.quartz.encoders.RelayUrlFormatter
|
||||
|
||||
@Composable
|
||||
fun DisplayNotifyMessages(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val openDialogMsg =
|
||||
accountViewModel.account.transientPaymentRequests.collectAsStateWithLifecycle(null)
|
||||
|
||||
openDialogMsg.value?.firstOrNull()?.let { request ->
|
||||
NotifyRequestDialog(
|
||||
title =
|
||||
stringRes(
|
||||
id = R.string.payment_required_title,
|
||||
RelayUrlFormatter.displayUrl(request.relayUrl),
|
||||
),
|
||||
textContent = request.description,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
) {
|
||||
accountViewModel.dismissPaymentRequest(request)
|
||||
}
|
||||
}
|
||||
}
|
@ -42,6 +42,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.richtext.ExpandableTextCutOffCalculator
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.getGradient
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@ -66,7 +67,7 @@ fun ExpandableRichTextViewer(
|
||||
id: String,
|
||||
callbackUri: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var showFullText by
|
||||
remember {
|
||||
|
@ -85,6 +85,8 @@ import com.vitorpamplona.amethyst.model.checkForHashtagWithIcon
|
||||
import com.vitorpamplona.amethyst.service.CachedRichTextParser
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.markdown.RenderContentAsMarkdown
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -115,7 +117,7 @@ fun RichTextViewer(
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
if (remember(content) { isMarkdown(content) }) {
|
||||
@ -129,7 +131,7 @@ fun RichTextViewer(
|
||||
@Preview
|
||||
@Composable
|
||||
fun RenderStrangeNamePreview() {
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
Column(modifier = Modifier.padding(10.dp)) {
|
||||
RenderRegular(
|
||||
@ -152,7 +154,7 @@ fun RenderStrangeNamePreview() {
|
||||
@Preview
|
||||
@Composable
|
||||
fun RenderRegularPreview() {
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
Column(modifier = Modifier.padding(10.dp)) {
|
||||
RenderRegular(
|
||||
@ -192,7 +194,7 @@ fun RenderRegularPreview() {
|
||||
@Preview
|
||||
@Composable
|
||||
fun RenderRegularPreview2() {
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
RenderRegular(
|
||||
"#Amethyst v0.84.1: ncryptsec support (NIP-49)",
|
||||
EmptyTagList,
|
||||
@ -229,7 +231,7 @@ fun RenderRegularPreview3() {
|
||||
arrayOf("proxy", "https://misskey.io/notes/9q0x6gtdysir03qh", "activitypub"),
|
||||
),
|
||||
)
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
RenderRegular(
|
||||
@ -266,7 +268,7 @@ private fun RenderRegular(
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
RenderRegular(content, tags, callbackUri) { word, state ->
|
||||
if (canPreview) {
|
||||
@ -359,7 +361,7 @@ private fun RenderWordWithoutPreview(
|
||||
state: RichTextViewerState,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
when (word) {
|
||||
// Don't preview Images
|
||||
@ -390,7 +392,7 @@ private fun RenderWordWithPreview(
|
||||
quotesLeft: Int,
|
||||
callbackUri: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
when (word) {
|
||||
is ImageSegment -> ZoomableContentView(word.segmentText, state, accountViewModel)
|
||||
@ -448,7 +450,7 @@ fun BechLink(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val loadedLink by produceCachedState(cache = accountViewModel.bechLinkCache, key = word)
|
||||
|
||||
@ -488,7 +490,7 @@ fun DisplayFullNote(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
NoteCompose(
|
||||
baseNote = note,
|
||||
@ -510,7 +512,7 @@ fun DisplayFullNote(
|
||||
@Composable
|
||||
fun HashTag(
|
||||
segment: HashTagSegment,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val primary = MaterialTheme.colorScheme.primary
|
||||
val background = MaterialTheme.colorScheme.onBackground
|
||||
@ -542,12 +544,12 @@ fun HashTag(
|
||||
modifier =
|
||||
remember {
|
||||
Modifier.clickable {
|
||||
nav("Hashtag/${segment.hashtag}")
|
||||
nav.nav("Hashtag/${segment.hashtag}")
|
||||
}
|
||||
},
|
||||
inlineContent =
|
||||
if (hashtagIcon != null) {
|
||||
mapOf("inlineContent" to InlineIcon(hashtagIcon))
|
||||
mapOf("inlineContent" to inlineIcon(hashtagIcon))
|
||||
} else {
|
||||
emptyMap()
|
||||
},
|
||||
@ -555,7 +557,7 @@ fun HashTag(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InlineIcon(hashtagIcon: HashtagIcon) =
|
||||
private fun inlineIcon(hashtagIcon: HashtagIcon) =
|
||||
InlineTextContent(inlinePlaceholder) {
|
||||
Icon(
|
||||
imageVector = hashtagIcon.icon,
|
||||
@ -569,7 +571,7 @@ private fun InlineIcon(hashtagIcon: HashtagIcon) =
|
||||
fun TagLink(
|
||||
word: HashIndexUserSegment,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadUser(baseUserHex = word.hex, accountViewModel) {
|
||||
if (it == null) {
|
||||
@ -610,7 +612,7 @@ fun TagLink(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadNote(baseNoteHex = word.hex, accountViewModel) {
|
||||
if (it == null) {
|
||||
@ -639,7 +641,7 @@ private fun DisplayNoteFromTag(
|
||||
quotesLeft: Int,
|
||||
accountViewModel: AccountViewModel,
|
||||
backgroundColor: MutableState<Color>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
if (canPreview && quotesLeft > 0) {
|
||||
NoteCompose(
|
||||
@ -662,7 +664,7 @@ private fun DisplayNoteFromTag(
|
||||
private fun DisplayUserFromTag(
|
||||
baseUser: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val meta by baseUser.live().userMetadataInfo.observeAsState(baseUser.info)
|
||||
|
||||
|
@ -24,7 +24,6 @@ import android.Manifest
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.WindowInsets
|
||||
import android.view.WindowManager
|
||||
import android.widget.FrameLayout
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
@ -54,7 +53,6 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.SideEffect
|
||||
@ -144,22 +142,6 @@ fun ZoomableImageDialog(
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(key1 = Unit) {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
view.windowInsetsController?.hide(
|
||||
WindowInsets.Type.systemBars(),
|
||||
)
|
||||
}
|
||||
|
||||
onDispose {
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
view.windowInsetsController?.show(
|
||||
WindowInsets.Type.systemBars(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Surface(modifier = Modifier.fillMaxSize()) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
|
||||
DialogContent(allImages, imageUrl, onDismiss, accountViewModel)
|
||||
|
@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.ui.components.DisplayFullNote
|
||||
import com.vitorpamplona.amethyst.ui.components.DisplayUser
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadUrlPreview
|
||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.LoadedBechLink
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font17SP
|
||||
@ -59,7 +60,7 @@ class MarkdownMediaRenderer(
|
||||
val backgroundColor: MutableState<Color>,
|
||||
val callbackUri: String? = null,
|
||||
val accountViewModel: AccountViewModel,
|
||||
val nav: (String) -> Unit,
|
||||
val nav: INav,
|
||||
) : MediaRenderer {
|
||||
val parser = RichTextParser()
|
||||
|
||||
|
@ -41,6 +41,8 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
|
||||
import com.vitorpamplona.amethyst.service.previews.UrlInfoItem
|
||||
import com.vitorpamplona.amethyst.ui.components.UrlPreviewState
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.MarkdownTextStyle
|
||||
@ -65,7 +67,7 @@ fun RenderContentAsMarkdown(
|
||||
backgroundColor: MutableState<Color>,
|
||||
callbackUri: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val uri = LocalUriHandler.current
|
||||
val onClick =
|
||||
@ -73,7 +75,7 @@ fun RenderContentAsMarkdown(
|
||||
{ link: String ->
|
||||
val route = uriToRoute(link)
|
||||
if (route != null) {
|
||||
nav(route)
|
||||
nav.nav(route)
|
||||
} else {
|
||||
runCatching { uri.openUri(link) }
|
||||
}
|
||||
@ -116,7 +118,7 @@ fun RenderContentAsMarkdown(
|
||||
fun RenderContentAsMarkdownPreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
ThemeComparisonRow {
|
||||
val background = MaterialTheme.colorScheme.background
|
||||
@ -168,7 +170,7 @@ fun RenderContentAsMarkdownPreview() {
|
||||
fun RenderContentAsMarkdownListsPreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
ThemeComparisonRow {
|
||||
val background = MaterialTheme.colorScheme.background
|
||||
@ -218,7 +220,7 @@ fun RenderContentAsMarkdownListsPreview() {
|
||||
fun RenderContentAsMarkdownCodePreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
ThemeComparisonRow {
|
||||
val background = MaterialTheme.colorScheme.background
|
||||
@ -272,7 +274,7 @@ fun RenderContentAsMarkdownCodePreview() {
|
||||
fun RenderContentAsMarkdownTablesPreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
ThemeComparisonRow {
|
||||
val background = MaterialTheme.colorScheme.background
|
||||
@ -313,7 +315,7 @@ fun RenderContentAsMarkdownTablesPreview() {
|
||||
fun RenderContentAsMarkdownFootNotesPreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
ThemeComparisonRow {
|
||||
val background = MaterialTheme.colorScheme.background
|
||||
@ -350,7 +352,7 @@ fun RenderContentAsMarkdownFootNotesPreview() {
|
||||
fun RenderContentAsMarkdownUserPreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
@ -412,7 +414,7 @@ fun RenderContentAsMarkdownUserPreview() {
|
||||
fun RenderContentAsMarkdownNotePreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
|
@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.dal
|
||||
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ZapReqResponse
|
||||
import com.vitorpamplona.quartz.events.LnZapEventInterface
|
||||
|
||||
class UserProfileZapsFeedFilter(
|
||||
|
@ -29,6 +29,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Composable
|
||||
@ -38,7 +39,7 @@ fun RefresheableFeedContentStateView(
|
||||
enablePullRefresh: Boolean = true,
|
||||
scrollStateKey: String? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
RefresheableBox(feedContentState, enablePullRefresh) {
|
||||
SaveableFeedContentState(feedContentState, scrollStateKey) { listState ->
|
||||
@ -88,7 +89,7 @@ fun RenderFeedContentState(
|
||||
feedContentState: FeedContentState,
|
||||
accountViewModel: AccountViewModel,
|
||||
listState: LazyListState,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
routeForLastRead: String?,
|
||||
onLoaded: @Composable (FeedState.Loaded) -> Unit = { FeedLoaded(it, listState, routeForLastRead, accountViewModel, nav) },
|
||||
onEmpty: @Composable () -> Unit = { FeedEmpty(feedContentState::invalidateData) },
|
||||
|
@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
@ -43,7 +44,7 @@ fun FeedLoaded(
|
||||
listState: LazyListState,
|
||||
routeForLastRead: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val items by loaded.feed.collectAsStateWithLifecycle()
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.navigation
|
||||
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.vitorpamplona.amethyst.debugState
|
||||
import com.vitorpamplona.amethyst.ui.note.AmethystIcon
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
||||
|
||||
@Composable
|
||||
fun AmethystClickableIcon() {
|
||||
val context = LocalContext.current
|
||||
|
||||
IconButton(
|
||||
onClick = { debugState(context) },
|
||||
) {
|
||||
AmethystIcon(Size40dp)
|
||||
}
|
||||
}
|
@ -50,7 +50,6 @@ import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
@ -118,17 +117,17 @@ fun IfKeyboardClosed(inner: @Composable () -> Unit) {
|
||||
|
||||
@Composable
|
||||
fun AppBottomBar(
|
||||
selectedRoute: Route?,
|
||||
accountViewModel: AccountViewModel,
|
||||
navEntryState: State<NavBackStackEntry?>,
|
||||
nav: (Route, Boolean) -> Unit,
|
||||
) {
|
||||
IfKeyboardClosed { RenderBottomMenu(accountViewModel, navEntryState, nav) }
|
||||
IfKeyboardClosed { RenderBottomMenu(selectedRoute, accountViewModel, nav) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderBottomMenu(
|
||||
selectedRoute: Route?,
|
||||
accountViewModel: AccountViewModel,
|
||||
navEntryState: State<NavBackStackEntry?>,
|
||||
nav: (Route, Boolean) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
@ -144,28 +143,12 @@ private fun RenderBottomMenu(
|
||||
)
|
||||
NavigationBar(tonalElevation = Size0dp) {
|
||||
bottomNavigationItems.forEach { item ->
|
||||
ObserveSelection(item, navEntryState) { selected ->
|
||||
HasNewItemsIcon(selected, item, accountViewModel, nav)
|
||||
}
|
||||
HasNewItemsIcon(item == selectedRoute, item, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ObserveSelection(
|
||||
route: Route,
|
||||
navEntryState: State<NavBackStackEntry?>,
|
||||
content: @Composable (Boolean) -> Unit,
|
||||
) {
|
||||
content(
|
||||
navEntryState.value
|
||||
?.destination
|
||||
?.route
|
||||
?.startsWith(route.base) ?: false,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.HasNewItemsIcon(
|
||||
selected: Boolean,
|
||||
|
@ -36,327 +36,201 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.util.Consumer
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.MainActivity
|
||||
import com.vitorpamplona.amethyst.ui.components.DisplayErrorMessages
|
||||
import com.vitorpamplona.amethyst.ui.components.DisplayNotifyMessages
|
||||
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountSwitcherAndLeftDrawerLayout
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.BookmarkListScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.DraftListScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.GeoHashScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HashtagScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HiddenUsersScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.LoadRedirectScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ProfileScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ThreadScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarks.BookmarkListScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChannelScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChatroomListScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChatroomScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChatroomScreenByAuthor
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.CommunityScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.CommunityScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.DiscoverScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.NIP90ContentDiscoveryScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.drafts.DraftListScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.DvmContentDiscoveryScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.GeoHashScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.HomeScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.SearchScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SecurityFiltersScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.threadview.ThreadScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.VideoScreen
|
||||
import com.vitorpamplona.amethyst.ui.uriToRoute
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.URLDecoder
|
||||
|
||||
fun NavBackStackEntry.id(): String? = arguments?.getString("id")
|
||||
|
||||
fun NavBackStackEntry.message(): String? =
|
||||
arguments?.getString("message")?.let {
|
||||
URLDecoder.decode(it, "utf-8")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AppNavigation(
|
||||
navController: NavHostController,
|
||||
accountViewModel: AccountViewModel,
|
||||
accountStateViewModel: AccountStateViewModel,
|
||||
sharedPreferencesViewModel: SharedPreferencesViewModel,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val nav =
|
||||
remember(navController) {
|
||||
{ route: String ->
|
||||
scope.launch {
|
||||
if (getRouteWithArguments(navController) != route) {
|
||||
navController.navigate(route)
|
||||
}
|
||||
}
|
||||
Unit
|
||||
}
|
||||
}
|
||||
val nav = rememberNav()
|
||||
|
||||
NavHost(
|
||||
navController,
|
||||
startDestination = Route.Home.route,
|
||||
enterTransition = { fadeIn(animationSpec = tween(200)) },
|
||||
exitTransition = { fadeOut(animationSpec = tween(200)) },
|
||||
) {
|
||||
Route.Home.let { route ->
|
||||
AccountSwitcherAndLeftDrawerLayout(accountViewModel, accountStateViewModel, nav) {
|
||||
NavHost(
|
||||
navController = nav.controller,
|
||||
startDestination = Route.Home.route,
|
||||
enterTransition = { fadeIn(animationSpec = tween(200)) },
|
||||
exitTransition = { fadeOut(animationSpec = tween(200)) },
|
||||
) {
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = { it ->
|
||||
val nip47 = it.arguments?.getString("nip47")
|
||||
Route.Home.route,
|
||||
Route.Home.arguments,
|
||||
) {
|
||||
val nip47 = it.arguments?.getString("nip47")
|
||||
|
||||
HomeScreen(
|
||||
newThreadsFeedState = accountViewModel.feedStates.homeNewThreads,
|
||||
repliesFeedState = accountViewModel.feedStates.homeReplies,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
nip47 = nip47,
|
||||
)
|
||||
HomeScreen(accountViewModel, nav, nip47)
|
||||
|
||||
if (nip47 != null) {
|
||||
LaunchedEffect(key1 = Unit) {
|
||||
launch {
|
||||
delay(1000)
|
||||
it.arguments?.remove("nip47")
|
||||
}
|
||||
if (nip47 != null) {
|
||||
LaunchedEffect(key1 = Unit) {
|
||||
launch {
|
||||
delay(1000)
|
||||
it.arguments?.remove("nip47")
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Message.route,
|
||||
content = {
|
||||
ChatroomListScreen(
|
||||
accountViewModel.feedStates.dmKnown,
|
||||
accountViewModel.feedStates.dmNew,
|
||||
composable(Route.Message.route) { ChatroomListScreen(accountViewModel, nav) }
|
||||
composable(Route.Video.route) { VideoScreen(accountViewModel, nav) }
|
||||
composable(Route.Discover.route) { DiscoverScreen(accountViewModel, nav) }
|
||||
composable(Route.Notification.route) { NotificationScreen(sharedPreferencesViewModel, accountViewModel, nav) }
|
||||
|
||||
composable(Route.Search.route) { SearchScreen(accountViewModel, nav) }
|
||||
|
||||
composable(Route.BlockedUsers.route, content = { SecurityFiltersScreen(accountViewModel, nav) })
|
||||
composable(Route.Bookmarks.route, content = { BookmarkListScreen(accountViewModel, nav) })
|
||||
composable(Route.Drafts.route, content = { DraftListScreen(accountViewModel, nav) })
|
||||
|
||||
composable(
|
||||
Route.ContentDiscovery.route,
|
||||
Route.ContentDiscovery.arguments,
|
||||
) {
|
||||
DvmContentDiscoveryScreen(it.id(), accountViewModel, nav)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Profile.route,
|
||||
Route.Profile.arguments,
|
||||
) {
|
||||
ProfileScreen(it.id(), accountViewModel, nav)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Note.route,
|
||||
Route.Note.arguments,
|
||||
) {
|
||||
ThreadScreen(it.id(), accountViewModel, nav)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Hashtag.route,
|
||||
Route.Hashtag.arguments,
|
||||
) {
|
||||
HashtagScreen(it.id(), accountViewModel, nav)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Geohash.route,
|
||||
Route.Geohash.arguments,
|
||||
) {
|
||||
GeoHashScreen(it.id(), accountViewModel, nav)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Community.route,
|
||||
Route.Community.arguments,
|
||||
) {
|
||||
CommunityScreen(it.id(), accountViewModel, nav)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Room.route,
|
||||
Route.Room.arguments,
|
||||
) {
|
||||
ChatroomScreen(
|
||||
roomId = it.id(),
|
||||
draftMessage = it.message(),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.RoomByAuthor.route,
|
||||
Route.RoomByAuthor.arguments,
|
||||
) {
|
||||
ChatroomScreenByAuthor(it.id(), null, accountViewModel, nav)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Channel.route,
|
||||
Route.Channel.arguments,
|
||||
) {
|
||||
ChannelScreen(
|
||||
channelId = it.id(),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Event.route,
|
||||
Route.Event.arguments,
|
||||
) {
|
||||
LoadRedirectScreen(
|
||||
eventId = it.id(),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
composable(
|
||||
Route.Settings.route,
|
||||
Route.Settings.arguments,
|
||||
) {
|
||||
SettingsScreen(
|
||||
sharedPreferencesViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Route.Video.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
VideoScreen(
|
||||
videoFeedContentState = accountViewModel.feedStates.videoFeed,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Discover.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
DiscoverScreen(
|
||||
discoveryContentNIP89FeedContentState = accountViewModel.feedStates.discoverDVMs,
|
||||
discoveryMarketplaceFeedContentState = accountViewModel.feedStates.discoverMarketplace,
|
||||
discoveryLiveFeedContentState = accountViewModel.feedStates.discoverLive,
|
||||
discoveryCommunityFeedContentState = accountViewModel.feedStates.discoverCommunities,
|
||||
discoveryChatFeedContentState = accountViewModel.feedStates.discoverPublicChats,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Search.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
SearchScreen(
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Notification.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
NotificationScreen(
|
||||
notifFeedContentState = accountViewModel.feedStates.notifications,
|
||||
notifSummaryState = accountViewModel.feedStates.notificationSummary,
|
||||
sharedPreferencesViewModel = sharedPreferencesViewModel,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
composable(Route.BlockedUsers.route, content = { HiddenUsersScreen(accountViewModel, nav) })
|
||||
composable(Route.Bookmarks.route, content = { BookmarkListScreen(accountViewModel, nav) })
|
||||
composable(Route.Drafts.route, content = { DraftListScreen(accountViewModel, nav) })
|
||||
|
||||
Route.ContentDiscovery.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
it.arguments?.getString("id")?.let { id ->
|
||||
NIP90ContentDiscoveryScreen(
|
||||
appDefinitionEventId = id,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Profile.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
ProfileScreen(
|
||||
userId = it.arguments?.getString("id"),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Note.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
ThreadScreen(
|
||||
noteId = it.arguments?.getString("id"),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Hashtag.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
HashtagScreen(
|
||||
tag = it.arguments?.getString("id"),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Geohash.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
GeoHashScreen(
|
||||
tag = it.arguments?.getString("id"),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Community.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
CommunityScreen(
|
||||
aTagHex = it.arguments?.getString("id"),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Room.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
val decodedMessage =
|
||||
it.arguments?.getString("message")?.let {
|
||||
URLDecoder.decode(it, "utf-8")
|
||||
}
|
||||
ChatroomScreen(
|
||||
roomId = it.arguments?.getString("id"),
|
||||
draftMessage = decodedMessage,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.RoomByAuthor.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
ChatroomScreenByAuthor(
|
||||
authorPubKeyHex = it.arguments?.getString("id"),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Channel.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
ChannelScreen(
|
||||
channelId = it.arguments?.getString("id"),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Event.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
LoadRedirectScreen(
|
||||
eventId = it.arguments?.getString("id"),
|
||||
accountViewModel = accountViewModel,
|
||||
navController = navController,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Route.Settings.let { route ->
|
||||
composable(
|
||||
route.route,
|
||||
route.arguments,
|
||||
content = {
|
||||
SettingsScreen(
|
||||
sharedPreferencesViewModel,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NavigateIfIntentRequested(nav.controller, accountViewModel)
|
||||
|
||||
DisplayErrorMessages(accountViewModel)
|
||||
DisplayNotifyMessages(accountViewModel, nav)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NavigateIfIntentRequested(
|
||||
navController: NavHostController,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val activity = LocalContext.current.getActivity()
|
||||
|
||||
var currentIntentNextPage by remember {
|
||||
@ -397,6 +271,8 @@ fun AppNavigation(
|
||||
}
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
DisposableEffect(activity) {
|
||||
val consumer =
|
||||
Consumer<Intent> { intent ->
|
||||
|
@ -20,554 +20,67 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.navigation
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.os.Debug
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
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
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.map
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import coil.Coil
|
||||
import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.service.NostrAccountDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrChannelDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrChatroomDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrCommunityDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrDiscoveryDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrGeohashDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrHashtagDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrSearchEventOrUserDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrSingleChannelDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrThreadDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrUserProfileDataSource
|
||||
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.note.AmethystIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadChannel
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadCityName
|
||||
import com.vitorpamplona.amethyst.ui.note.NonClickableUserPictures
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.SearchIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.types.LongCommunityHeader
|
||||
import com.vitorpamplona.amethyst.ui.note.types.ShortCommunityHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.CodeName
|
||||
import com.vitorpamplona.amethyst.ui.screen.CodeNameType
|
||||
import com.vitorpamplona.amethyst.ui.screen.CommunityName
|
||||
import com.vitorpamplona.amethyst.ui.screen.FollowListState
|
||||
import com.vitorpamplona.amethyst.ui.screen.GeoHashName
|
||||
import com.vitorpamplona.amethyst.ui.screen.HashtagName
|
||||
import com.vitorpamplona.amethyst.ui.screen.Name
|
||||
import com.vitorpamplona.amethyst.ui.screen.PeopleListName
|
||||
import com.vitorpamplona.amethyst.ui.screen.ResourceName
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.DislayGeoTagHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.GeoHashActionOptions
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HashtagActionOptions
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.LoadRoom
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.LoadRoomByAuthor
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.LoadUser
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.LongChannelHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.LongRoomHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.RoomNameOnlyDisplay
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ShortChannelHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.observeAppDefinition
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.BottomTopHeight
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
import com.vitorpamplona.amethyst.ui.theme.HeaderPictureModifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size22Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size34dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size40dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.ammolite.relays.Client
|
||||
import com.vitorpamplona.ammolite.relays.RelayPool
|
||||
import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun AppTopBar(
|
||||
navEntryState: State<NavBackStackEntry?>,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
val currentRoute by
|
||||
remember(navEntryState.value) {
|
||||
derivedStateOf {
|
||||
navEntryState.value
|
||||
?.destination
|
||||
?.route
|
||||
?.substringBefore("?")
|
||||
}
|
||||
}
|
||||
|
||||
val id by
|
||||
remember(navEntryState.value) {
|
||||
derivedStateOf { navEntryState.value?.arguments?.getString("id") }
|
||||
}
|
||||
|
||||
RenderTopRouteBar(currentRoute, id, accountViewModel.feedStates.feedListOptions, openDrawer, accountViewModel, nav, navPopBack)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderTopRouteBar(
|
||||
currentRoute: String?,
|
||||
id: String?,
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
when (currentRoute) {
|
||||
Route.Home.base -> HomeTopBar(followLists, openDrawer, accountViewModel, nav)
|
||||
Route.Video.base -> StoriesTopBar(followLists, openDrawer, accountViewModel, nav)
|
||||
Route.Discover.base -> DiscoveryTopBar(followLists, openDrawer, accountViewModel, nav)
|
||||
Route.Notification.base -> NotificationTopBar(followLists, openDrawer, accountViewModel, nav)
|
||||
Route.Settings.base -> TopBarWithBackButton(stringRes(id = R.string.application_preferences), navPopBack)
|
||||
Route.Bookmarks.base -> TopBarWithBackButton(stringRes(id = R.string.bookmarks), navPopBack)
|
||||
Route.Drafts.base -> TopBarWithBackButton(stringRes(id = R.string.drafts), navPopBack)
|
||||
|
||||
else -> {
|
||||
if (id != null) {
|
||||
when (currentRoute) {
|
||||
Route.Channel.base -> ChannelTopBar(id, accountViewModel, nav, navPopBack)
|
||||
Route.RoomByAuthor.base -> RoomByAuthorTopBar(id, accountViewModel, nav, navPopBack)
|
||||
Route.Room.base -> RoomTopBar(id, accountViewModel, nav, navPopBack)
|
||||
Route.Community.base -> CommunityTopBar(id, accountViewModel, nav, navPopBack)
|
||||
Route.Hashtag.base -> HashTagTopBar(id, accountViewModel, navPopBack)
|
||||
Route.Geohash.base -> GeoHashTopBar(id, accountViewModel, navPopBack)
|
||||
Route.Note.base -> ThreadTopBar(id, accountViewModel, navPopBack)
|
||||
Route.ContentDiscovery.base -> DvmTopBar(id, accountViewModel, nav, navPopBack)
|
||||
else -> MainTopBar(openDrawer, accountViewModel, nav)
|
||||
}
|
||||
} else {
|
||||
MainTopBar(openDrawer, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ThreadTopBar(
|
||||
id: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = { Text(stringRes(id = R.string.thread_title)) },
|
||||
popBack = navPopBack,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GeoHashTopBar(
|
||||
tag: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = {
|
||||
DislayGeoTagHeader(tag, remember { Modifier.weight(1f) })
|
||||
GeoHashActionOptions(tag, accountViewModel)
|
||||
},
|
||||
popBack = navPopBack,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HashTagTopBar(
|
||||
tag: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = {
|
||||
Text(
|
||||
remember(tag) { "#$tag" },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
|
||||
HashtagActionOptions(tag, accountViewModel)
|
||||
},
|
||||
popBack = navPopBack,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CommunityTopBar(
|
||||
id: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
LoadAddressableNote(aTagHex = id, accountViewModel) { baseNote ->
|
||||
if (baseNote != null) {
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = { ShortCommunityHeader(baseNote, accountViewModel, nav) },
|
||||
extendableRow = {
|
||||
Column(Modifier.verticalScroll(rememberScrollState())) {
|
||||
LongCommunityHeader(baseNote = baseNote, accountViewModel = accountViewModel, nav = nav)
|
||||
}
|
||||
},
|
||||
popBack = navPopBack,
|
||||
)
|
||||
} else {
|
||||
Spacer(BottomTopHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoomTopBar(
|
||||
id: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
LoadRoom(roomId = id, accountViewModel) { room ->
|
||||
if (room != null) {
|
||||
RenderRoomTopBar(room, accountViewModel, nav, navPopBack)
|
||||
} else {
|
||||
Spacer(BottomTopHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DvmTopBar(
|
||||
appDefinitionId: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = {
|
||||
LoadNote(baseNoteHex = appDefinitionId, accountViewModel = accountViewModel) { appDefinitionNote ->
|
||||
if (appDefinitionNote != null) {
|
||||
val card = observeAppDefinition(appDefinitionNote)
|
||||
|
||||
card.cover?.let {
|
||||
AsyncImage(
|
||||
model = it,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier =
|
||||
Modifier
|
||||
.size(Size34dp)
|
||||
.clip(shape = CircleShape),
|
||||
)
|
||||
} ?: run { NoteAuthorPicture(baseNote = appDefinitionNote, size = Size34dp, accountViewModel = accountViewModel) }
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
Text(
|
||||
text = card.name,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
popBack = navPopBack,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoomByAuthorTopBar(
|
||||
id: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
LoadRoomByAuthor(authorPubKeyHex = id, accountViewModel) { room ->
|
||||
if (room != null) {
|
||||
RenderRoomTopBar(room, accountViewModel, nav, navPopBack)
|
||||
} else {
|
||||
Spacer(BottomTopHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderRoomTopBar(
|
||||
room: ChatroomKey,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
if (room.users.size == 1) {
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = {
|
||||
LoadUser(baseUserHex = room.users.first(), accountViewModel) { baseUser ->
|
||||
if (baseUser != null) {
|
||||
ClickableUserPicture(
|
||||
baseUser = baseUser,
|
||||
accountViewModel = accountViewModel,
|
||||
size = Size34dp,
|
||||
)
|
||||
|
||||
Spacer(modifier = DoubleHorzSpacer)
|
||||
|
||||
UsernameDisplay(baseUser, Modifier.weight(1f), fontWeight = FontWeight.Normal, accountViewModel = accountViewModel)
|
||||
}
|
||||
}
|
||||
},
|
||||
extendableRow = {
|
||||
LoadUser(baseUserHex = room.users.first(), accountViewModel) {
|
||||
if (it != null) {
|
||||
UserCompose(
|
||||
baseUser = it,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
popBack = navPopBack,
|
||||
)
|
||||
} else {
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = {
|
||||
NonClickableUserPictures(
|
||||
room = room,
|
||||
accountViewModel = accountViewModel,
|
||||
size = Size34dp,
|
||||
)
|
||||
|
||||
RoomNameOnlyDisplay(
|
||||
room,
|
||||
Modifier
|
||||
.padding(start = 10.dp)
|
||||
.weight(1f),
|
||||
fontWeight = FontWeight.Normal,
|
||||
accountViewModel,
|
||||
)
|
||||
},
|
||||
extendableRow = {
|
||||
LongRoomHeader(room = room, accountViewModel = accountViewModel, nav = nav)
|
||||
},
|
||||
popBack = navPopBack,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChannelTopBar(
|
||||
id: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
navPopBack: () -> Unit,
|
||||
) {
|
||||
LoadChannel(baseChannelHex = id, accountViewModel) { baseChannel ->
|
||||
FlexibleTopBarWithBackButton(
|
||||
title = {
|
||||
ShortChannelHeader(
|
||||
baseChannel = baseChannel,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
showFlag = true,
|
||||
)
|
||||
},
|
||||
extendableRow = {
|
||||
LongChannelHeader(baseChannel = baseChannel, accountViewModel = accountViewModel, nav = nav)
|
||||
},
|
||||
popBack = navPopBack,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StoriesTopBar(
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) {
|
||||
val list by accountViewModel.account.settings.defaultStoriesFollowList
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
FollowListWithRoutes(
|
||||
followListsModel = followLists,
|
||||
listName = list,
|
||||
) { listName ->
|
||||
accountViewModel.account.settings.changeDefaultStoriesFollowList(listName.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HomeTopBar(
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) {
|
||||
val list by accountViewModel.account.settings.defaultHomeFollowList
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
FollowListWithRoutes(
|
||||
followListsModel = followLists,
|
||||
listName = list,
|
||||
) { listName ->
|
||||
if (listName.type == CodeNameType.ROUTE) {
|
||||
nav(listName.code)
|
||||
} else {
|
||||
accountViewModel.account.settings.changeDefaultHomeFollowList(listName.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NotificationTopBar(
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) {
|
||||
val list by accountViewModel.account.settings.defaultNotificationFollowList
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
FollowListWithoutRoutes(
|
||||
followListsModel = followLists,
|
||||
listName = list,
|
||||
) { listName ->
|
||||
accountViewModel.account.settings.changeDefaultNotificationFollowList(listName.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DiscoveryTopBar(
|
||||
followLists: FollowListState,
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) {
|
||||
val list by accountViewModel.account.settings.defaultDiscoveryFollowList
|
||||
.collectAsStateWithLifecycle()
|
||||
|
||||
FollowListWithoutRoutes(
|
||||
followListsModel = followLists,
|
||||
listName = list,
|
||||
) { listName ->
|
||||
accountViewModel.account.settings.changeDefaultDiscoveryFollowList(listName.code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainTopBar(
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
GenericMainTopBar(openDrawer, accountViewModel, nav) { AmethystClickableIcon() }
|
||||
GenericMainTopBar(accountViewModel, nav) { AmethystClickableIcon() }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun GenericMainTopBar(
|
||||
openDrawer: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Column(modifier = BottomTopHeight) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
LoggedInUserPictureDrawer(accountViewModel, openDrawer)
|
||||
},
|
||||
actions = { SearchButton { nav(Route.Search.route) } },
|
||||
)
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchButton(onClick: () -> Unit) {
|
||||
IconButton(
|
||||
onClick = onClick,
|
||||
) {
|
||||
SearchIcon(modifier = Size22Modifier, MaterialTheme.colorScheme.placeholderText)
|
||||
}
|
||||
TopAppBar(
|
||||
scrollBehavior = rememberHeightDecreaser(),
|
||||
title = {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
LoggedInUserPictureDrawer(accountViewModel, nav::openDrawer)
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { nav.nav(Route.Search.route) }) {
|
||||
SearchIcon(modifier = Size22Modifier, MaterialTheme.colorScheme.placeholderText)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -575,14 +88,14 @@ private fun LoggedInUserPictureDrawer(
|
||||
accountViewModel: AccountViewModel,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val profilePicture by
|
||||
accountViewModel.account
|
||||
.userProfile()
|
||||
.live()
|
||||
.profilePictureChanges
|
||||
.observeAsState()
|
||||
|
||||
IconButton(onClick = onClick) {
|
||||
val profilePicture by
|
||||
accountViewModel.account
|
||||
.userProfile()
|
||||
.live()
|
||||
.profilePictureChanges
|
||||
.observeAsState()
|
||||
|
||||
RobohashFallbackAsyncImage(
|
||||
robot = accountViewModel.userProfile().pubkeyHex,
|
||||
model = profilePicture,
|
||||
@ -603,8 +116,9 @@ fun FollowListWithRoutes(
|
||||
) {
|
||||
val allLists by followListsModel.kind3GlobalPeopleRoutes.collectAsStateWithLifecycle()
|
||||
|
||||
SimpleTextSpinner(
|
||||
FeedFilterSpinner(
|
||||
placeholderCode = listName,
|
||||
explainer = stringRes(R.string.select_list_to_filter),
|
||||
options = allLists,
|
||||
onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.kind3Follow) },
|
||||
)
|
||||
@ -618,383 +132,10 @@ fun FollowListWithoutRoutes(
|
||||
) {
|
||||
val allLists by followListsModel.kind3GlobalPeople.collectAsStateWithLifecycle()
|
||||
|
||||
SimpleTextSpinner(
|
||||
FeedFilterSpinner(
|
||||
placeholderCode = listName,
|
||||
explainer = stringRes(R.string.select_list_to_filter),
|
||||
options = allLists,
|
||||
onSelect = { onChange(allLists.getOrNull(it) ?: followListsModel.kind3Follow) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SimpleTextSpinner(
|
||||
placeholderCode: String,
|
||||
options: ImmutableList<CodeName>,
|
||||
onSelect: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
var optionsShowing by remember { mutableStateOf(false) }
|
||||
|
||||
val context = LocalContext.current
|
||||
val selectAnOption =
|
||||
stringRes(
|
||||
id = R.string.select_an_option,
|
||||
)
|
||||
|
||||
var currentText by
|
||||
remember(placeholderCode, options) {
|
||||
mutableStateOf(
|
||||
options.firstOrNull { it.code == placeholderCode }?.name?.name(context) ?: selectAnOption,
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier,
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Spacer(modifier = Size20Modifier)
|
||||
Text(currentText)
|
||||
Icon(
|
||||
imageVector = Icons.Default.ExpandMore,
|
||||
null,
|
||||
modifier = Size20Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.matchParentSize()
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
) {
|
||||
optionsShowing = true
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (optionsShowing) {
|
||||
options.isNotEmpty().also {
|
||||
SpinnerSelectionDialog(
|
||||
options = options,
|
||||
onDismiss = { optionsShowing = false },
|
||||
onSelect = {
|
||||
currentText = options[it].name.name(context)
|
||||
optionsShowing = false
|
||||
onSelect(it)
|
||||
},
|
||||
) {
|
||||
RenderOption(it.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderOption(option: Name) {
|
||||
when (option) {
|
||||
is GeoHashName -> {
|
||||
LoadCityName(option.geoHashTag) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(text = "/g/$it", color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
is HashtagName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(text = option.name(), color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
is ResourceName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(id = option.resourceId),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
is PeopleListName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val noteState by
|
||||
option.note
|
||||
.live()
|
||||
.metadata
|
||||
.observeAsState()
|
||||
|
||||
val name = (noteState?.note?.event as? PeopleListEvent)?.nameOrTitle() ?: option.note.dTag() ?: ""
|
||||
|
||||
Text(text = name, color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
is CommunityName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val name by
|
||||
option.note
|
||||
.live()
|
||||
.metadata
|
||||
.map { "/n/" + ((it.note as? AddressableNote)?.dTag() ?: "") }
|
||||
.observeAsState()
|
||||
|
||||
Text(text = name ?: "", color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TopBarWithBackButton(
|
||||
caption: String,
|
||||
popBack: () -> Unit,
|
||||
) {
|
||||
Column(modifier = BottomTopHeight) {
|
||||
TopAppBar(
|
||||
title = { Text(caption) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = popBack,
|
||||
modifier = Modifier,
|
||||
) {
|
||||
ArrowBackIcon()
|
||||
}
|
||||
},
|
||||
actions = {},
|
||||
)
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FlexibleTopBarWithBackButton(
|
||||
title: @Composable RowScope.() -> Unit,
|
||||
extendableRow: (@Composable () -> Unit)? = null,
|
||||
popBack: () -> Unit,
|
||||
) {
|
||||
Column {
|
||||
MyExtensibleTopAppBar(
|
||||
title = title,
|
||||
extendableRow = extendableRow,
|
||||
navigationIcon = { IconButton(onClick = popBack) { ArrowBackIcon() } },
|
||||
actions = {},
|
||||
)
|
||||
HorizontalDivider(thickness = DividerThickness)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AmethystClickableIcon() {
|
||||
val context = LocalContext.current
|
||||
|
||||
IconButton(
|
||||
onClick = { debugState(context) },
|
||||
) {
|
||||
AmethystIcon(Size40dp)
|
||||
}
|
||||
}
|
||||
|
||||
fun debugState(context: Context) {
|
||||
Client
|
||||
.allSubscriptions()
|
||||
.forEach { Log.d("STATE DUMP", "${it.key} ${it.value.joinToString { it.filter.toDebugJson() }}") }
|
||||
|
||||
NostrAccountDataSource.printCounter()
|
||||
NostrChannelDataSource.printCounter()
|
||||
NostrChatroomDataSource.printCounter()
|
||||
NostrChatroomListDataSource.printCounter()
|
||||
NostrCommunityDataSource.printCounter()
|
||||
NostrDiscoveryDataSource.printCounter()
|
||||
NostrHashtagDataSource.printCounter()
|
||||
NostrGeohashDataSource.printCounter()
|
||||
NostrHomeDataSource.printCounter()
|
||||
NostrSearchEventOrUserDataSource.printCounter()
|
||||
NostrSingleChannelDataSource.printCounter()
|
||||
NostrSingleEventDataSource.printCounter()
|
||||
NostrSingleUserDataSource.printCounter()
|
||||
NostrThreadDataSource.printCounter()
|
||||
NostrUserProfileDataSource.printCounter()
|
||||
NostrVideoDataSource.printCounter()
|
||||
|
||||
val totalMemoryMb = Runtime.getRuntime().totalMemory() / (1024 * 1024)
|
||||
val freeMemoryMb = Runtime.getRuntime().freeMemory() / (1024 * 1024)
|
||||
val maxMemoryMb = Runtime.getRuntime().maxMemory() / (1024 * 1024)
|
||||
|
||||
val jvmHeapAllocatedMb = totalMemoryMb - freeMemoryMb
|
||||
|
||||
Log.d("STATE DUMP", "Total Heap Allocated: " + jvmHeapAllocatedMb + "/" + maxMemoryMb + " MB")
|
||||
|
||||
val nativeHeap = Debug.getNativeHeapAllocatedSize() / (1024 * 1024)
|
||||
val maxNative = Debug.getNativeHeapSize() / (1024 * 1024)
|
||||
|
||||
Log.d("STATE DUMP", "Total Native Heap Allocated: " + nativeHeap + "/" + maxNative + " MB")
|
||||
|
||||
val activityManager: ActivityManager? = context.getSystemService()
|
||||
if (activityManager != null) {
|
||||
val isLargeHeap = (context.applicationInfo.flags and ApplicationInfo.FLAG_LARGE_HEAP) != 0
|
||||
val memClass = if (isLargeHeap) activityManager.largeMemoryClass else activityManager.memoryClass
|
||||
|
||||
Log.d("STATE DUMP", "Memory Class " + memClass + " MB (largeHeap $isLargeHeap)")
|
||||
}
|
||||
|
||||
Log.d("STATE DUMP", "Connected Relays: " + RelayPool.connectedRelays())
|
||||
|
||||
val imageLoader = Coil.imageLoader(context)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Image Disk Cache ${(imageLoader.diskCache?.size ?: 0) / (1024 * 1024)}/${(imageLoader.diskCache?.maxSize ?: 0) / (1024 * 1024)} MB",
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Image Memory Cache ${(imageLoader.memoryCache?.size ?: 0) / (1024 * 1024)}/${(imageLoader.memoryCache?.maxSize ?: 0) / (1024 * 1024)} MB",
|
||||
)
|
||||
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Notes: " +
|
||||
LocalCache.notes.filter { _, it -> it.liveSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.notes.filter { _, it -> it.flowSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.notes.filter { _, it -> it.event != null }.size +
|
||||
" / " +
|
||||
LocalCache.notes.size(),
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Addressables: " +
|
||||
LocalCache.addressables.filter { _, it -> it.liveSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.addressables.filter { _, it -> it.flowSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.addressables.filter { _, it -> it.event != null }.size +
|
||||
" / " +
|
||||
LocalCache.addressables.size(),
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Users: " +
|
||||
LocalCache.users.filter { _, it -> it.liveSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.users.filter { _, it -> it.flowSet != null }.size +
|
||||
" / " +
|
||||
LocalCache.users.filter { _, it -> it.latestMetadata != null }.size +
|
||||
" / " +
|
||||
LocalCache.users.size(),
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Deletion Events: " +
|
||||
LocalCache.deletionIndex.size(),
|
||||
)
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Observable Events: " +
|
||||
LocalCache.observablesByKindAndETag.size +
|
||||
" / " +
|
||||
LocalCache.observablesByKindAndAuthor.size,
|
||||
)
|
||||
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Spam: " +
|
||||
LocalCache.antiSpam.spamMessages.size() + " / " + LocalCache.antiSpam.recentMessages.size(),
|
||||
)
|
||||
|
||||
Log.d(
|
||||
"STATE DUMP",
|
||||
"Memory used by Events: " +
|
||||
LocalCache.notes.sumOfLong { _, note -> note.event?.countMemory() ?: 0L } / (1024 * 1024) +
|
||||
" MB",
|
||||
)
|
||||
|
||||
val qttNotes = LocalCache.notes.countByGroup { _, it -> it.event?.kind() }
|
||||
val qttAddressables = LocalCache.addressables.countByGroup { _, it -> it.event?.kind() }
|
||||
|
||||
val bytesNotes =
|
||||
LocalCache.notes
|
||||
.sumByGroup(groupMap = { _, it -> it.event?.kind() }, sumOf = { _, it -> it.event?.countMemory() ?: 0L })
|
||||
val bytesAddressables =
|
||||
LocalCache.addressables
|
||||
.sumByGroup(groupMap = { _, it -> it.event?.kind() }, sumOf = { _, it -> it.event?.countMemory() ?: 0L })
|
||||
|
||||
qttNotes.toList().sortedByDescending { bytesNotes.get(it.first) }.forEach { (kind, qtt) ->
|
||||
Log.d("STATE DUMP", "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesNotes.get(kind)?.div((1024 * 1024))}MB ")
|
||||
}
|
||||
qttAddressables.toList().sortedByDescending { bytesNotes.get(it.first) }.forEach { (kind, qtt) ->
|
||||
Log.d("STATE DUMP", "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesAddressables.get(kind)?.div((1024 * 1024))}MB ")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MyExtensibleTopAppBar(
|
||||
title: @Composable RowScope.() -> Unit,
|
||||
extendableRow: (@Composable () -> Unit)? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
navigationIcon: @Composable (() -> Unit)? = null,
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
|
||||
Column(
|
||||
Modifier.clickable { expanded.value = !expanded.value },
|
||||
) {
|
||||
Row(modifier = BottomTopHeight) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
title()
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
navigationIcon = {
|
||||
if (navigationIcon == null) {
|
||||
Spacer(TitleInsetWithoutIcon)
|
||||
} else {
|
||||
Row(TitleIconModifier, verticalAlignment = Alignment.CenterVertically) {
|
||||
navigationIcon()
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
|
||||
if (expanded.value && extendableRow != null) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column { extendableRow() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this should probably be part of the touch target of the start and end icons, clarify this
|
||||
private val AppBarHorizontalPadding = 4.dp
|
||||
|
||||
// Start inset for the title when there is no navigation icon provided
|
||||
private val TitleInsetWithoutIcon = Modifier.width(16.dp - AppBarHorizontalPadding)
|
||||
|
||||
// Start inset for the title when there is a navigation icon provided
|
||||
private val TitleIconModifier = Modifier.width(48.dp - AppBarHorizontalPadding)
|
||||
|
@ -29,11 +29,15 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
@ -44,7 +48,6 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Send
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DrawerState
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@ -92,10 +95,10 @@ import com.vitorpamplona.amethyst.ui.components.ClickableText
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadStatuses
|
||||
import com.vitorpamplona.amethyst.ui.qrcode.ShowQRDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountBackupDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ConnectOrbotDialog
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.ShowQRDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||
@ -115,23 +118,20 @@ import com.vitorpamplona.ammolite.relays.RelayPoolStatus
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
import com.vitorpamplona.quartz.encoders.HexKey
|
||||
import com.vitorpamplona.quartz.events.ImmutableListOfLists
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun DrawerContent(
|
||||
nav: (String) -> Unit,
|
||||
drawerState: DrawerState,
|
||||
nav: INav,
|
||||
openSheet: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val onClickUser = {
|
||||
nav("User/${accountViewModel.userProfile().pubkeyHex}")
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
Unit
|
||||
nav.nav(routeFor(accountViewModel.userProfile()))
|
||||
nav.closeDrawer()
|
||||
}
|
||||
|
||||
ModalDrawerSheet(
|
||||
windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Bottom + WindowInsetsSides.Start),
|
||||
drawerContainerColor = MaterialTheme.colorScheme.background,
|
||||
drawerTonalElevation = 0.dp,
|
||||
) {
|
||||
@ -148,7 +148,7 @@ fun DrawerContent(
|
||||
)
|
||||
|
||||
Column(drawerSpacing) {
|
||||
EditStatusBoxes(accountViewModel.account.userProfile(), accountViewModel, drawerState)
|
||||
EditStatusBoxes(accountViewModel.account.userProfile(), accountViewModel, nav)
|
||||
}
|
||||
|
||||
FollowingAndFollowerCounts(accountViewModel.account, onClickUser)
|
||||
@ -160,7 +160,6 @@ fun DrawerContent(
|
||||
|
||||
ListContent(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
drawerState,
|
||||
openSheet,
|
||||
accountViewModel,
|
||||
nav,
|
||||
@ -170,7 +169,6 @@ fun DrawerContent(
|
||||
|
||||
BottomContent(
|
||||
accountViewModel.account.userProfile(),
|
||||
drawerState,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
@ -265,16 +263,16 @@ fun ProfileContentTemplate(
|
||||
private fun EditStatusBoxes(
|
||||
baseAccountUser: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
drawerState: DrawerState,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadStatuses(user = baseAccountUser, accountViewModel) { statuses ->
|
||||
if (statuses.isEmpty()) {
|
||||
StatusEditBar(accountViewModel = accountViewModel, drawerState = drawerState)
|
||||
StatusEditBar(accountViewModel = accountViewModel, nav = nav)
|
||||
} else {
|
||||
statuses.forEach {
|
||||
val originalStatus by it.live().content.observeAsState()
|
||||
|
||||
StatusEditBar(originalStatus, it.address, accountViewModel, drawerState = drawerState)
|
||||
StatusEditBar(originalStatus, it.address, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -285,14 +283,14 @@ fun StatusEditBar(
|
||||
savedStatus: String? = null,
|
||||
tag: ATag? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
drawerState: DrawerState,
|
||||
nav: INav,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val currentStatus = remember { mutableStateOf(savedStatus ?: "") }
|
||||
val hasChanged = remember { derivedStateOf { currentStatus.value != (savedStatus ?: "") } }
|
||||
LaunchedEffect(drawerState.isClosed) {
|
||||
if (drawerState.isClosed) {
|
||||
LaunchedEffect(nav.drawerState.isClosed) {
|
||||
if (nav.drawerState.isClosed) {
|
||||
focusManager.clearFocus(true)
|
||||
}
|
||||
}
|
||||
@ -434,14 +432,12 @@ fun WatchFollower(
|
||||
@Composable
|
||||
fun ListContent(
|
||||
modifier: Modifier,
|
||||
drawerState: DrawerState,
|
||||
openSheet: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val route = remember(accountViewModel) { "User/${accountViewModel.userProfile().pubkeyHex}" }
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var wantsToEditRelays by remember { mutableStateOf(false) }
|
||||
var editMediaServers by remember { mutableStateOf(false) }
|
||||
|
||||
@ -465,7 +461,6 @@ fun ListContent(
|
||||
icon = Route.Profile.icon,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
nav = nav,
|
||||
drawerState = drawerState,
|
||||
route = route,
|
||||
)
|
||||
|
||||
@ -474,7 +469,6 @@ fun ListContent(
|
||||
icon = Route.Bookmarks.icon,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
nav = nav,
|
||||
drawerState = drawerState,
|
||||
route = Route.Bookmarks.route,
|
||||
)
|
||||
|
||||
@ -483,24 +477,23 @@ fun ListContent(
|
||||
icon = Route.Drafts.icon,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
nav = nav,
|
||||
drawerState = drawerState,
|
||||
route = Route.Drafts.route,
|
||||
)
|
||||
|
||||
IconRowRelays(
|
||||
accountViewModel = accountViewModel,
|
||||
onClick = {
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav.closeDrawer()
|
||||
wantsToEditRelays = true
|
||||
},
|
||||
)
|
||||
|
||||
IconRow(
|
||||
title = "Media Servers",
|
||||
title = stringRes(R.string.media_servers),
|
||||
icon = androidx.media3.ui.R.drawable.exo_icon_repeat_all,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
onClick = {
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav.closeDrawer()
|
||||
editMediaServers = true
|
||||
},
|
||||
)
|
||||
@ -510,7 +503,6 @@ fun ListContent(
|
||||
icon = Route.BlockedUsers.icon,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
nav = nav,
|
||||
drawerState = drawerState,
|
||||
route = Route.BlockedUsers.route,
|
||||
)
|
||||
|
||||
@ -520,7 +512,7 @@ fun ListContent(
|
||||
icon = R.drawable.ic_key,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
onClick = {
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav.closeDrawer()
|
||||
backupDialogOpen = true
|
||||
},
|
||||
)
|
||||
@ -536,14 +528,14 @@ fun ListContent(
|
||||
icon = R.drawable.ic_tor,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
onLongClick = {
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav.closeDrawer()
|
||||
conectOrbotDialogOpen = true
|
||||
},
|
||||
onClick = {
|
||||
if (checked) {
|
||||
disconnectTorDialog = true
|
||||
} else {
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav.closeDrawer()
|
||||
conectOrbotDialogOpen = true
|
||||
}
|
||||
},
|
||||
@ -554,7 +546,6 @@ fun ListContent(
|
||||
icon = Route.Settings.icon,
|
||||
tint = MaterialTheme.colorScheme.onBackground,
|
||||
nav = nav,
|
||||
drawerState = drawerState,
|
||||
route = Route.Settings.route,
|
||||
)
|
||||
|
||||
@ -660,18 +651,16 @@ fun NavigationRow(
|
||||
title: String,
|
||||
icon: Int,
|
||||
tint: Color,
|
||||
nav: (String) -> Unit,
|
||||
drawerState: DrawerState,
|
||||
nav: INav,
|
||||
route: String,
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
IconRow(
|
||||
title,
|
||||
icon,
|
||||
tint,
|
||||
onClick = {
|
||||
nav(route)
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav.closeDrawer()
|
||||
nav.nav(route)
|
||||
},
|
||||
)
|
||||
}
|
||||
@ -754,9 +743,8 @@ fun IconRowRelays(
|
||||
@Composable
|
||||
fun BottomContent(
|
||||
user: User,
|
||||
drawerState: DrawerState,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
@ -788,8 +776,8 @@ fun BottomContent(
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
nav("Note/${BuildConfig.RELEASE_NOTES_ID}")
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav.nav("Note/${BuildConfig.RELEASE_NOTES_ID}")
|
||||
nav.closeDrawer()
|
||||
},
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
)
|
||||
@ -797,7 +785,7 @@ fun BottomContent(
|
||||
IconButton(
|
||||
onClick = {
|
||||
dialogOpen = true
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav.closeDrawer()
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
@ -816,8 +804,8 @@ fun BottomContent(
|
||||
accountViewModel,
|
||||
onScan = {
|
||||
dialogOpen = false
|
||||
coroutineScope.launch { drawerState.close() }
|
||||
nav(it)
|
||||
nav.closeDrawer()
|
||||
nav.nav(it)
|
||||
},
|
||||
onClose = { dialogOpen = false },
|
||||
)
|
||||
|
@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.navigation
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ExpandMore
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.map
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadCityName
|
||||
import com.vitorpamplona.amethyst.ui.screen.CodeName
|
||||
import com.vitorpamplona.amethyst.ui.screen.CommunityName
|
||||
import com.vitorpamplona.amethyst.ui.screen.GeoHashName
|
||||
import com.vitorpamplona.amethyst.ui.screen.HashtagName
|
||||
import com.vitorpamplona.amethyst.ui.screen.Name
|
||||
import com.vitorpamplona.amethyst.ui.screen.PeopleListName
|
||||
import com.vitorpamplona.amethyst.ui.screen.ResourceName
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SpinnerSelectionDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.quartz.events.PeopleListEvent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun FeedFilterSpinner(
|
||||
placeholderCode: String,
|
||||
explainer: String,
|
||||
options: ImmutableList<CodeName>,
|
||||
onSelect: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var optionsShowing by remember { mutableStateOf(false) }
|
||||
|
||||
val context = LocalContext.current
|
||||
val selectAnOption =
|
||||
stringRes(
|
||||
id = R.string.select_an_option,
|
||||
)
|
||||
|
||||
var currentText by
|
||||
remember(placeholderCode, options) {
|
||||
mutableStateOf(
|
||||
options.firstOrNull { it.code == placeholderCode }?.name?.name(context) ?: selectAnOption,
|
||||
)
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier,
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Spacer(modifier = Size20Modifier)
|
||||
Text(currentText)
|
||||
Icon(
|
||||
imageVector = Icons.Default.ExpandMore,
|
||||
contentDescription = explainer,
|
||||
modifier = Size20Modifier,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.matchParentSize()
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
) {
|
||||
optionsShowing = true
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (optionsShowing) {
|
||||
options.isNotEmpty().also {
|
||||
SpinnerSelectionDialog(
|
||||
options = options,
|
||||
onDismiss = { optionsShowing = false },
|
||||
onSelect = {
|
||||
currentText = options[it].name.name(context)
|
||||
optionsShowing = false
|
||||
onSelect(it)
|
||||
},
|
||||
) {
|
||||
RenderOption(it.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderOption(option: Name) {
|
||||
when (option) {
|
||||
is GeoHashName -> {
|
||||
LoadCityName(option.geoHashTag) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(text = "/g/$it", color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
is HashtagName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(text = option.name(), color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
is ResourceName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(id = option.resourceId),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
is PeopleListName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val noteState by
|
||||
option.note
|
||||
.live()
|
||||
.metadata
|
||||
.observeAsState()
|
||||
|
||||
val name = (noteState?.note?.event as? PeopleListEvent)?.nameOrTitle() ?: option.note.dTag() ?: ""
|
||||
|
||||
Text(text = name, color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
is CommunityName -> {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val name by
|
||||
option.note
|
||||
.live()
|
||||
.metadata
|
||||
.map { "/n/" + ((it.note as? AddressableNote)?.dTag() ?: "") }
|
||||
.observeAsState()
|
||||
|
||||
Text(text = name ?: "", color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.navigation
|
||||
|
||||
import androidx.compose.animation.core.AnimationSpec
|
||||
import androidx.compose.animation.core.DecayAnimationSpec
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.TopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import com.vitorpamplona.amethyst.ui.theme.TopBarSize
|
||||
|
||||
// This is a hack to decrease the height for the
|
||||
// default TopBar without having to reimplement it.
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun rememberHeightDecreaser(): TopAppBarScrollBehavior {
|
||||
val height =
|
||||
LocalDensity.current.run {
|
||||
TopBarSize.toPx()
|
||||
}
|
||||
|
||||
return remember(height) {
|
||||
HeightDecreaser(height)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
class HeightDecreaser(
|
||||
height: Float,
|
||||
) : TopAppBarScrollBehavior {
|
||||
override val state: TopAppBarState =
|
||||
TopAppBarState(
|
||||
initialHeightOffsetLimit = -Float.MAX_VALUE,
|
||||
initialHeightOffset = 0f,
|
||||
initialContentOffset = 0f,
|
||||
).also {
|
||||
it.heightOffset = height
|
||||
}
|
||||
|
||||
override val isPinned: Boolean = true
|
||||
override val snapAnimationSpec: AnimationSpec<Float>? = null
|
||||
override val flingAnimationSpec: DecayAnimationSpec<Float>? = null
|
||||
override var nestedScrollConnection =
|
||||
object : NestedScrollConnection {
|
||||
override fun onPostScroll(
|
||||
consumed: Offset,
|
||||
available: Offset,
|
||||
source: NestedScrollSource,
|
||||
): Offset = available
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.navigation
|
||||
|
||||
import androidx.compose.material3.DrawerState
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@Composable
|
||||
fun rememberNav(): Nav {
|
||||
val navController = rememberNavController()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
return remember(navController, scope) {
|
||||
Nav(navController, scope)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberExtendedNav(
|
||||
nav: INav,
|
||||
onClose: () -> Unit,
|
||||
): INav = nav.onNavigate(onClose)
|
||||
|
||||
@Stable
|
||||
interface INav {
|
||||
val drawerState: DrawerState
|
||||
|
||||
fun nav(route: String)
|
||||
|
||||
fun newStack(route: String)
|
||||
|
||||
fun popBack()
|
||||
|
||||
fun popUpTo(
|
||||
route: String,
|
||||
upTo: String,
|
||||
)
|
||||
|
||||
fun closeDrawer()
|
||||
|
||||
fun openDrawer()
|
||||
}
|
||||
|
||||
@Stable
|
||||
class Nav(
|
||||
val controller: NavHostController,
|
||||
val scope: CoroutineScope,
|
||||
) : INav {
|
||||
override val drawerState = DrawerState(DrawerValue.Closed)
|
||||
|
||||
override fun closeDrawer() {
|
||||
scope.launch { drawerState.close() }
|
||||
}
|
||||
|
||||
override fun openDrawer() {
|
||||
scope.launch { drawerState.open() }
|
||||
}
|
||||
|
||||
override fun nav(route: String) {
|
||||
scope.launch {
|
||||
if (getRouteWithArguments(controller) != route) {
|
||||
controller.navigate(route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun newStack(route: String) {
|
||||
scope.launch {
|
||||
controller.navigate(route) {
|
||||
popUpTo(Route.Home.route)
|
||||
launchSingleTop = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun popBack() {
|
||||
scope.launch {
|
||||
controller.navigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
override fun popUpTo(
|
||||
route: String,
|
||||
upTo: String,
|
||||
) {
|
||||
scope.launch {
|
||||
controller.navigate(route) { popUpTo(route) { inclusive = true } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
object EmptyNav : INav {
|
||||
override val drawerState = DrawerState(DrawerValue.Closed)
|
||||
|
||||
override fun closeDrawer() {
|
||||
runBlocking {
|
||||
drawerState.close()
|
||||
}
|
||||
}
|
||||
|
||||
override fun openDrawer() {
|
||||
runBlocking {
|
||||
drawerState.open()
|
||||
}
|
||||
}
|
||||
|
||||
override fun nav(route: String) {
|
||||
}
|
||||
|
||||
override fun newStack(route: String) {
|
||||
}
|
||||
|
||||
override fun popBack() {
|
||||
}
|
||||
|
||||
override fun popUpTo(
|
||||
route: String,
|
||||
upTo: String,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
fun INav.onNavigate(runOnNavigate: () -> Unit): INav = ObservableNavigate(this, runOnNavigate)
|
||||
|
||||
class ObservableNavigate(
|
||||
val nav: INav,
|
||||
val onNavigate: () -> Unit,
|
||||
) : INav {
|
||||
override val drawerState: DrawerState = nav.drawerState
|
||||
|
||||
override fun nav(route: String) {
|
||||
onNavigate()
|
||||
nav.nav(route)
|
||||
}
|
||||
|
||||
override fun newStack(route: String) {
|
||||
onNavigate()
|
||||
nav.newStack(route)
|
||||
}
|
||||
|
||||
override fun popBack() {
|
||||
onNavigate()
|
||||
nav.popBack()
|
||||
}
|
||||
|
||||
override fun popUpTo(
|
||||
route: String,
|
||||
upTo: String,
|
||||
) {
|
||||
onNavigate()
|
||||
nav.popUpTo(route, upTo)
|
||||
}
|
||||
|
||||
override fun closeDrawer() {
|
||||
nav.closeDrawer()
|
||||
}
|
||||
|
||||
override fun openDrawer() {
|
||||
nav.openDrawer()
|
||||
}
|
||||
}
|
@ -239,15 +239,6 @@ sealed class Route(
|
||||
route = "Settings",
|
||||
icon = R.drawable.ic_settings,
|
||||
)
|
||||
|
||||
companion object {
|
||||
val InvertedLayouts =
|
||||
setOf(
|
||||
Channel.route,
|
||||
Room.route,
|
||||
RoomByAuthor.route,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
open class LatestItem {
|
||||
|
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.navigation
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||
|
||||
@Composable
|
||||
fun TopBarExtensibleWithBackButton(
|
||||
title: @Composable RowScope.() -> Unit,
|
||||
extendableRow: (@Composable () -> Unit)? = null,
|
||||
popBack: () -> Unit,
|
||||
) {
|
||||
MyExtensibleTopAppBar(
|
||||
title = title,
|
||||
extendableRow = extendableRow,
|
||||
navigationIcon = { IconButton(onClick = popBack) { ArrowBackIcon() } },
|
||||
actions = {},
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MyExtensibleTopAppBar(
|
||||
title: @Composable RowScope.() -> Unit,
|
||||
extendableRow: (@Composable () -> Unit)? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
navigationIcon: @Composable (() -> Unit)? = null,
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
|
||||
Column(
|
||||
Modifier.clickable { expanded.value = !expanded.value },
|
||||
) {
|
||||
TopAppBar(
|
||||
scrollBehavior = rememberHeightDecreaser(),
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
title()
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
navigationIcon = {
|
||||
if (navigationIcon == null) {
|
||||
Spacer(TitleInsetWithoutIcon)
|
||||
} else {
|
||||
Row(TitleIconModifier, verticalAlignment = Alignment.CenterVertically) {
|
||||
navigationIcon()
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = actions,
|
||||
)
|
||||
|
||||
if (expanded.value && extendableRow != null) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column { extendableRow() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this should probably be part of the touch target of the start and end icons, clarify this
|
||||
private val AppBarHorizontalPadding = 4.dp
|
||||
|
||||
// Start inset for the title when there is no navigation icon provided
|
||||
private val TitleInsetWithoutIcon = Modifier.width(16.dp - AppBarHorizontalPadding)
|
||||
|
||||
// Start inset for the title when there is a navigation icon provided
|
||||
private val TitleIconModifier = Modifier.width(48.dp - AppBarHorizontalPadding)
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Copyright (c) 2024 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.navigation
|
||||
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TopBarWithBackButton(
|
||||
caption: String,
|
||||
popBack: () -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
scrollBehavior = rememberHeightDecreaser(),
|
||||
title = { Text(caption) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = popBack,
|
||||
modifier = Modifier,
|
||||
) {
|
||||
ArrowBackIcon()
|
||||
}
|
||||
},
|
||||
actions = {},
|
||||
)
|
||||
}
|
@ -43,6 +43,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
|
||||
import com.vitorpamplona.amethyst.ui.note.types.BadgeDisplay
|
||||
@ -58,7 +59,7 @@ fun BadgeCompose(
|
||||
isInnerNote: Boolean = false,
|
||||
routeForLastRead: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by likeSetCard.note
|
||||
.live()
|
||||
@ -87,7 +88,7 @@ fun BadgeCompose(
|
||||
routeFor(
|
||||
note,
|
||||
accountViewModel.userProfile(),
|
||||
)?.let { nav(it) }
|
||||
)?.let { nav.nav(it) }
|
||||
},
|
||||
),
|
||||
) {
|
||||
|
@ -39,6 +39,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@ -96,7 +98,7 @@ fun BlankNote(
|
||||
@Preview
|
||||
fun HiddenNotePreview() {
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
|
||||
ThemeComparisonColumn(
|
||||
toPreview = {
|
||||
@ -117,7 +119,7 @@ fun HiddenNote(
|
||||
isHiddenAuthor: Boolean,
|
||||
accountViewModel: AccountViewModel,
|
||||
modifier: Modifier = Modifier,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Column(modifier = modifier, horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
|
@ -27,6 +27,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Composable
|
||||
@ -36,7 +37,7 @@ fun CheckHiddenFeedWatchBlockAndReport(
|
||||
showHiddenWarning: Boolean,
|
||||
ignoreAllBlocksAndReports: Boolean = false,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
normalNote: @Composable (canPreview: Boolean) -> Unit,
|
||||
) {
|
||||
if (ignoreAllBlocksAndReports) {
|
||||
@ -55,7 +56,7 @@ fun WatchBlockAndReport(
|
||||
showHiddenWarning: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
normalNote: @Composable (canPreview: Boolean) -> Unit,
|
||||
) {
|
||||
val isHidden by accountViewModel.createIsHiddenFlow(note).collectAsStateWithLifecycle()
|
||||
|
@ -70,6 +70,7 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.layouts.LeftPictureLayout
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChannelHeader
|
||||
@ -77,7 +78,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.EndedFlag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.LiveFlag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.OfflineFlag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ScheduledFlag
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.observeAppDefinition
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.observeAppDefinition
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CheckIfUrlIsOnline
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.showAmountAxis
|
||||
@ -116,7 +117,7 @@ fun ChannelCardCompose(
|
||||
forceEventKind: Int?,
|
||||
isHiddenFeed: Boolean = false,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
WatchNoteEvent(baseNote = baseNote, accountViewModel = accountViewModel) {
|
||||
if (forceEventKind == null || baseNote.event?.kind() == forceEventKind) {
|
||||
@ -148,7 +149,7 @@ fun NormalChannelCard(
|
||||
modifier: Modifier = Modifier,
|
||||
parentBackgroundColor: MutableState<Color>? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup ->
|
||||
CheckNewAndRenderChannelCard(
|
||||
@ -171,7 +172,7 @@ private fun CheckNewAndRenderChannelCard(
|
||||
parentBackgroundColor: MutableState<Color>? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
showPopup: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val backgroundColor =
|
||||
calculateBackgroundColor(
|
||||
@ -201,7 +202,7 @@ private fun CheckNewAndRenderChannelCard(
|
||||
fun InnerChannelCardWithReactions(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
when (baseNote.event) {
|
||||
is LiveActivitiesEvent -> {
|
||||
@ -226,7 +227,7 @@ fun InnerChannelCardWithReactions(
|
||||
fun InnerCardRow(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(StdPadding) {
|
||||
SensitivityWarning(
|
||||
@ -246,7 +247,7 @@ fun InnerCardRow(
|
||||
fun InnerCardBox(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(HalfPadding) {
|
||||
SensitivityWarning(
|
||||
@ -262,7 +263,7 @@ fun InnerCardBox(
|
||||
private fun RenderNoteRow(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
when (baseNote.event) {
|
||||
is LiveActivitiesEvent -> {
|
||||
@ -291,7 +292,7 @@ data class ClassifiedsThumb(
|
||||
fun RenderClassifiedsThumb(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? ClassifiedsEvent ?: return
|
||||
|
||||
@ -414,7 +415,7 @@ data class LiveActivityCard(
|
||||
fun RenderLiveActivityThumb(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? LiveActivitiesEvent ?: return
|
||||
|
||||
@ -547,7 +548,7 @@ data class DVMCard(
|
||||
fun RenderCommunitiesThumb(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? CommunityDefinitionEvent ?: return
|
||||
|
||||
@ -757,7 +758,7 @@ private fun LoadParticipants(
|
||||
fun RenderContentDVMThumb(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
// downloads user metadata to pre-load the NIP-65 relays.
|
||||
val user =
|
||||
@ -845,7 +846,7 @@ fun RenderContentDVMThumb(
|
||||
fun RenderChannelThumb(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? ChannelCreateEvent ?: return
|
||||
|
||||
@ -859,7 +860,7 @@ fun RenderChannelThumb(
|
||||
baseNote: Note,
|
||||
channel: Channel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val channelUpdates by channel.live.observeAsState()
|
||||
|
||||
|
@ -37,6 +37,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.NoteDropDownMenu
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -50,7 +51,7 @@ fun MessageSetCompose(
|
||||
routeForLastRead: String,
|
||||
showHidden: Boolean = false,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val baseNote = remember { messageSetCard.note }
|
||||
|
||||
@ -80,7 +81,7 @@ fun MessageSetCompose(
|
||||
routeFor(
|
||||
baseNote,
|
||||
accountViewModel.userProfile(),
|
||||
)?.let { nav(it) }
|
||||
)?.let { nav.nav(it) }
|
||||
}
|
||||
},
|
||||
onLongClick = enablePopup,
|
||||
|
@ -65,6 +65,7 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.authorRouteFor
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.NoteDropDownMenu
|
||||
@ -101,7 +102,7 @@ fun MultiSetCompose(
|
||||
routeForLastRead: String,
|
||||
showHidden: Boolean = false,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val baseNote = remember { multiSetCard.note }
|
||||
|
||||
@ -123,7 +124,7 @@ fun MultiSetCompose(
|
||||
.background(backgroundColor.value)
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
scope.launch { routeFor(baseNote, accountViewModel.userProfile())?.let { nav(it) } }
|
||||
scope.launch { routeFor(baseNote, accountViewModel.userProfile())?.let { nav.nav(it) } }
|
||||
},
|
||||
onLongClick = { popupExpanded.value = true },
|
||||
).padding(
|
||||
@ -163,7 +164,7 @@ private fun Galeries(
|
||||
multiSetCard: MultiSetCard,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
if (multiSetCard.zapEvents.isNotEmpty()) {
|
||||
DecryptAndRenderZapGallery(multiSetCard, backgroundColor, accountViewModel, nav)
|
||||
@ -184,7 +185,7 @@ private fun Galeries(
|
||||
fun RenderLikeGallery(
|
||||
reactionType: String,
|
||||
likeEvents: ImmutableList<Note>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
if (likeEvents.isNotEmpty()) {
|
||||
@ -226,7 +227,7 @@ fun DecryptAndRenderZapGallery(
|
||||
multiSetCard: MultiSetCard,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val zapEvents by
|
||||
produceState(initialValue = accountViewModel.cachedDecryptAmountMessageInGroup(multiSetCard.zapEvents)) {
|
||||
@ -241,7 +242,7 @@ fun RenderZapGallery(
|
||||
zapEvents: ImmutableList<ZapAmountCommentNotification>,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
Box(
|
||||
@ -259,7 +260,7 @@ fun RenderZapGallery(
|
||||
@Composable
|
||||
fun RenderBoostGallery(
|
||||
boostEvents: ImmutableList<Note>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Row(
|
||||
@ -280,7 +281,7 @@ fun RenderBoostGallery(
|
||||
@Composable
|
||||
fun RenderBoostGallery(
|
||||
noteToGetBoostEvents: NoteState,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Row(
|
||||
@ -321,7 +322,7 @@ fun MapZaps(
|
||||
fun AuthorGalleryZaps(
|
||||
authorNotes: ImmutableList<ZapAmountCommentNotification>,
|
||||
backgroundColor: MutableState<Color>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Column(modifier = StdStartPadding) {
|
||||
@ -367,9 +368,9 @@ private fun ParseAuthorCommentAndAmount(
|
||||
|
||||
fun click(
|
||||
content: ZapAmountCommentNotification,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
content.user?.let { nav(routeFor(it)) }
|
||||
content.user?.let { nav.nav(routeFor(it)) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -377,7 +378,7 @@ private fun RenderState(
|
||||
content: ZapAmountCommentNotification,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.clickable { click(content, nav) },
|
||||
@ -404,7 +405,7 @@ val commentTextSize = 12.sp
|
||||
private fun DisplayAuthorCommentAndAmount(
|
||||
authorComment: ZapAmountCommentNotification,
|
||||
backgroundColor: MutableState<Color>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Box(modifier = Size35Modifier, contentAlignment = Alignment.BottomCenter) {
|
||||
@ -446,7 +447,7 @@ fun CrossfadeToDisplayAmount(amount: String) {
|
||||
fun CrossfadeToDisplayComment(
|
||||
comment: String,
|
||||
backgroundColor: MutableState<Color>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
TranslatableRichTextViewer(
|
||||
@ -466,7 +467,7 @@ fun CrossfadeToDisplayComment(
|
||||
@Composable
|
||||
fun AuthorGallery(
|
||||
authorNotes: ImmutableList<Note>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Column(modifier = StdStartPadding) {
|
||||
@ -478,7 +479,7 @@ fun AuthorGallery(
|
||||
@Composable
|
||||
fun AuthorGallery(
|
||||
noteToGetBoostEvents: NoteState,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Column(modifier = StdStartPadding) {
|
||||
@ -491,10 +492,10 @@ fun AuthorGallery(
|
||||
@Composable
|
||||
private fun BoxedAuthor(
|
||||
note: Note,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Box(modifier = Size35Modifier.clickable(onClick = { nav(authorRouteFor(note)) })) {
|
||||
Box(modifier = Size35Modifier.clickable(onClick = { nav.nav(authorRouteFor(note)) })) {
|
||||
WatchAuthorWithBlank(note, Size35Modifier, accountViewModel) { author ->
|
||||
WatchUserMetadataAndFollowsAndRenderUserProfilePictureOrDefaultAuthor(
|
||||
author,
|
||||
|
@ -54,6 +54,7 @@ import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadStatuses
|
||||
@ -116,7 +117,7 @@ fun ObserveDisplayNip05Status(
|
||||
baseNote: Note,
|
||||
columnModifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
WatchAuthor(baseNote = baseNote) {
|
||||
ObserveDisplayNip05Status(it, columnModifier, accountViewModel, nav)
|
||||
@ -128,7 +129,7 @@ fun ObserveDisplayNip05Status(
|
||||
baseUser: User,
|
||||
columnModifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val nip05 by baseUser.live().nip05Changes.observeAsState(baseUser.nip05())
|
||||
|
||||
@ -158,7 +159,7 @@ private fun VerifyAndDisplayNIP05OrStatusLine(
|
||||
baseUser: User,
|
||||
columnModifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(modifier = columnModifier) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
@ -188,7 +189,7 @@ private fun VerifyAndDisplayNIP05OrStatusLine(
|
||||
fun ObserveRotateStatuses(
|
||||
statuses: ImmutableList<AddressableNote>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
ObserveAllStatusesToAvoidSwitchigAllTheTime(statuses)
|
||||
|
||||
@ -211,7 +212,7 @@ fun ObserveAllStatusesToAvoidSwitchigAllTheTime(statuses: ImmutableList<Addressa
|
||||
fun RotateStatuses(
|
||||
statuses: ImmutableList<AddressableNote>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var indexToDisplay by remember(statuses) { mutableIntStateOf(0) }
|
||||
|
||||
@ -242,7 +243,7 @@ fun DisplayUsersNpub(npub: String) {
|
||||
fun DisplayStatus(
|
||||
addressableNote: AddressableNote,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by addressableNote.live().metadata.observeAsState()
|
||||
val noteEvent = noteState?.note?.event ?: return
|
||||
@ -266,7 +267,7 @@ fun DisplayStatusInner(
|
||||
nostrATag: ATag?,
|
||||
nostrHexID: String?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
when (type) {
|
||||
"music" ->
|
||||
@ -311,7 +312,7 @@ fun DisplayStatusInner(
|
||||
routeFor(
|
||||
note,
|
||||
accountViewModel.userProfile(),
|
||||
)?.let { nav(it) }
|
||||
)?.let { nav.nav(it) }
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
@ -333,7 +334,7 @@ fun DisplayStatusInner(
|
||||
routeFor(
|
||||
it,
|
||||
accountViewModel.userProfile(),
|
||||
)?.let { nav(it) }
|
||||
)?.let { nav.nav(it) }
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
|
@ -60,6 +60,7 @@ import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.layouts.GenericRepostLayout
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.BoostedMark
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayEditStatus
|
||||
@ -204,7 +205,7 @@ fun NoteCompose(
|
||||
quotesLeft: Int,
|
||||
parentBackgroundColor: MutableState<Color>? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
WatchNoteEvent(
|
||||
baseNote = baseNote,
|
||||
@ -250,7 +251,7 @@ fun AcceptableNote(
|
||||
quotesLeft: Int,
|
||||
parentBackgroundColor: MutableState<Color>? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
if (isQuotedNote || isBoostedNote) {
|
||||
when (baseNote.event) {
|
||||
@ -382,7 +383,7 @@ private fun CheckNewAndRenderNote(
|
||||
parentBackgroundColor: MutableState<Color>? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
showPopup: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val backgroundColor =
|
||||
calculateBackgroundColor(
|
||||
@ -423,7 +424,7 @@ fun ClickableNote(
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
showPopup: () -> Unit,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val updatedModifier =
|
||||
@ -437,7 +438,7 @@ fun ClickableNote(
|
||||
} else {
|
||||
baseNote
|
||||
}
|
||||
routeFor(redirectToNote, accountViewModel.userProfile())?.let { nav(it) }
|
||||
routeFor(redirectToNote, accountViewModel.userProfile())?.let { nav.nav(it) }
|
||||
},
|
||||
onLongClick = showPopup,
|
||||
).background(backgroundColor.value)
|
||||
@ -457,7 +458,7 @@ fun InnerNoteWithReactions(
|
||||
canPreview: Boolean,
|
||||
quotesLeft: Int,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val notBoostedNorQuote = !isBoostedNote && !isQuotedNote
|
||||
val editState = observeEdits(baseNote = baseNote, accountViewModel = accountViewModel)
|
||||
@ -539,7 +540,7 @@ fun NoteBody(
|
||||
backgroundColor: MutableState<Color>,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
FirstUserInfoRow(
|
||||
baseNote = baseNote,
|
||||
@ -594,7 +595,7 @@ private fun RenderNoteRow(
|
||||
unPackReply: Boolean,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
when (val noteEvent = baseNote.event) {
|
||||
is AppDefinitionEvent -> RenderAppDefinition(baseNote, accountViewModel, nav)
|
||||
@ -819,7 +820,7 @@ fun RenderDraft(
|
||||
unPackReply: Boolean,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
ObserveDraftEvent(note, accountViewModel) {
|
||||
val edits = remember { mutableStateOf(GenericLoadable.Empty<EditState>()) }
|
||||
@ -850,7 +851,7 @@ fun RenderRepost(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
note.replyTo?.lastOrNull { it.event !is CommunityDefinitionEvent }?.let {
|
||||
NoteCompose(
|
||||
@ -880,7 +881,7 @@ fun ReplyNoteComposition(
|
||||
replyingDirectlyTo: Note,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
NoteCompose(
|
||||
baseNote = replyingDirectlyTo,
|
||||
@ -900,7 +901,7 @@ fun SecondUserInfoRow(
|
||||
note: Note,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event ?: return
|
||||
val noteAuthor = note.author ?: return
|
||||
@ -981,7 +982,7 @@ fun FirstUserInfoRow(
|
||||
showAuthorPicture: Boolean,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Row(verticalAlignment = CenterVertically, modifier = UserNameRowHeight) {
|
||||
val isRepost = baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent
|
||||
@ -1090,7 +1091,7 @@ fun observeEdits(
|
||||
private fun BadgeBox(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
if (accountViewModel.settings.featureSet == FeatureSetType.COMPLETE) {
|
||||
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
|
||||
@ -1104,7 +1105,7 @@ private fun BadgeBox(
|
||||
@Composable
|
||||
private fun RenderAuthorImages(
|
||||
baseNote: Note,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
if (baseNote.event is RepostEvent || baseNote.event is GenericRepostEvent) {
|
||||
@ -1161,7 +1162,7 @@ private fun RepostNoteAuthorPicture(
|
||||
baseNote: Note,
|
||||
baseRepost: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
GenericRepostLayout(
|
||||
baseAuthorPicture = {
|
||||
|
@ -87,6 +87,8 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.components.SelectTextDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ReportNoteDialog
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@ -153,7 +155,7 @@ fun LongPressToQuickAction(
|
||||
note = baseNote,
|
||||
onDismiss = { popupExpanded.value = false },
|
||||
accountViewModel = accountViewModel,
|
||||
nav = {},
|
||||
nav = EmptyNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -174,7 +176,7 @@ fun LongPressToQuickActionGallery(
|
||||
note = baseNote,
|
||||
onDismiss = { popupExpanded.value = false },
|
||||
accountViewModel = accountViewModel,
|
||||
nav = {},
|
||||
nav = EmptyNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -185,7 +187,7 @@ fun NoteQuickActionMenuGallery(
|
||||
note: Note,
|
||||
onDismiss: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
DeleteFromGalleryDialog(note, accountViewModel) {
|
||||
onDismiss()
|
||||
@ -197,7 +199,7 @@ fun NoteQuickActionMenu(
|
||||
note: Note,
|
||||
onDismiss: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val editDraftDialog = remember { mutableStateOf(false) }
|
||||
|
||||
@ -208,7 +210,7 @@ fun NoteQuickActionMenu(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
draft = note,
|
||||
nav = { },
|
||||
nav = EmptyNav,
|
||||
)
|
||||
}
|
||||
|
||||
@ -227,7 +229,7 @@ fun NoteQuickActionMenu(
|
||||
onDismiss: () -> Unit,
|
||||
onWantsToEditDraft: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val showSelectTextDialog = remember { mutableStateOf(false) }
|
||||
val showDeleteAlertDialog = remember { mutableStateOf(false) }
|
||||
|
@ -73,6 +73,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.StringToastMsg
|
||||
@ -99,7 +100,7 @@ fun PollNote(
|
||||
canPreview: Boolean,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val pollViewModel: PollNoteViewModel = viewModel(key = "PollNoteViewModel${baseNote.idHex}")
|
||||
|
||||
@ -122,7 +123,7 @@ fun PollNote(
|
||||
canPreview: Boolean,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
WatchZapsAndUpdateTallies(baseNote, pollViewModel)
|
||||
|
||||
@ -157,7 +158,7 @@ private fun OptionNote(
|
||||
accountViewModel: AccountViewModel,
|
||||
canPreview: Boolean,
|
||||
backgroundColor: MutableState<Color>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val tags = remember(baseNote) { baseNote.event?.tags()?.toImmutableListOfLists() ?: EmptyTagList }
|
||||
|
||||
@ -225,7 +226,7 @@ private fun RenderOptionAfterVote(
|
||||
tags: ImmutableListOfLists<String>,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
@ -295,7 +296,7 @@ private fun RenderOptionBeforeVote(
|
||||
tags: ImmutableListOfLists<String>,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
@ -331,7 +332,7 @@ fun ZapVote(
|
||||
nonClickablePrepend: @Composable () -> Unit,
|
||||
clickablePrepend: @Composable () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val isLoggedUser by remember { derivedStateOf { accountViewModel.isLoggedUser(baseNote.author) } }
|
||||
|
||||
@ -460,7 +461,7 @@ fun ZapVote(
|
||||
title = toast.title,
|
||||
textContent = toast.msg,
|
||||
onClickStartMessage = {
|
||||
baseNote.author?.let { nav(routeToMessage(it, toast.msg, accountViewModel)) }
|
||||
baseNote.author?.let { nav.nav(routeToMessage(it, toast.msg, accountViewModel)) }
|
||||
},
|
||||
onDismiss = { showErrorMessageDialog = null },
|
||||
)
|
||||
|
@ -105,6 +105,7 @@ import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.types.EditState
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -156,7 +157,7 @@ fun ReactionsRow(
|
||||
addPadding: Boolean,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val wantsToSeeReactions = remember(baseNote) { mutableStateOf(false) }
|
||||
|
||||
@ -178,7 +179,7 @@ private fun InnerReactionRow(
|
||||
wantsToSeeReactions: MutableState<Boolean>,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
GenericInnerReactionRow(
|
||||
showReactionDetail = showReactionDetail,
|
||||
@ -446,7 +447,7 @@ private fun RenderShowIndividualReactionsButton(
|
||||
@Composable
|
||||
private fun ReactionDetailGallery(
|
||||
baseNote: Note,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val defaultBackgroundColor = MaterialTheme.colorScheme.background
|
||||
@ -479,7 +480,7 @@ private fun ReactionDetailGallery(
|
||||
@Composable
|
||||
private fun WatchBoostsAndRenderGallery(
|
||||
baseNote: Note,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val boostsEvents by baseNote.live().boosts.observeAsState()
|
||||
@ -498,7 +499,7 @@ private fun WatchBoostsAndRenderGallery(
|
||||
@Composable
|
||||
private fun WatchReactionsAndRenderGallery(
|
||||
baseNote: Note,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val reactionsState by baseNote.live().reactions.observeAsState()
|
||||
@ -521,7 +522,7 @@ private fun WatchReactionsAndRenderGallery(
|
||||
private fun WatchZapAndRenderGallery(
|
||||
baseNote: Note,
|
||||
backgroundColor: MutableState<Color>,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
val zapsState by baseNote.live().zaps.observeAsState()
|
||||
@ -553,7 +554,7 @@ private fun BoostWithDialog(
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var wantsToQuote by remember { mutableStateOf<Note?>(null) }
|
||||
var wantsToFork by remember { mutableStateOf<Note?>(null) }
|
||||
@ -605,7 +606,7 @@ private fun ReplyReactionWithDialog(
|
||||
baseNote: Note,
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var wantsToReplyTo by remember { mutableStateOf<Note?>(null) }
|
||||
|
||||
@ -830,7 +831,7 @@ fun LikeReaction(
|
||||
baseNote: Note,
|
||||
grayTint: Color,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
iconSize: Dp = Size18dp,
|
||||
heartSizeModifier: Modifier = Size18Modifier,
|
||||
iconFontSize: TextUnit = Font14SP,
|
||||
@ -983,7 +984,7 @@ fun ZapReaction(
|
||||
iconSize: Dp = Size20dp,
|
||||
iconSizeModifier: Modifier = Size20Modifier,
|
||||
animationSize: Dp = 14.dp,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var wantsToZap by remember { mutableStateOf(false) }
|
||||
var wantsToChangeZapAmount by remember { mutableStateOf(false) }
|
||||
@ -1061,7 +1062,7 @@ fun ZapReaction(
|
||||
baseNote.author?.let {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val route = routeToMessage(it, msg, accountViewModel)
|
||||
nav(route)
|
||||
nav.nav(route)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -52,6 +52,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ShowMoreRelaysButtonBoxModifer
|
||||
@ -64,7 +65,7 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
fun RelayBadges(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
|
||||
@ -87,7 +88,7 @@ fun RenderAllRelayList(
|
||||
modifier: Modifier = Modifier,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteRelays by baseNote
|
||||
.flow()
|
||||
@ -105,7 +106,7 @@ fun RenderClosedRelayList(
|
||||
modifier: Modifier = Modifier,
|
||||
verticalAlignment: Alignment.Vertical = Alignment.Top,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Row(modifier, verticalAlignment = verticalAlignment) {
|
||||
WatchAndRenderRelay(baseNote, 0, accountViewModel, nav)
|
||||
@ -119,7 +120,7 @@ fun WatchAndRenderRelay(
|
||||
baseNote: Note,
|
||||
relayIndex: Int,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteRelays by baseNote
|
||||
.flow()
|
||||
|
@ -56,6 +56,7 @@ import com.vitorpamplona.amethyst.service.Nip11Retriever
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.RelayInformationDialog
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
|
||||
@ -70,7 +71,7 @@ import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
|
||||
public fun RelayBadgesHorizontal(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
|
||||
@ -117,7 +118,7 @@ fun ChatRelayExpandButton(onClick: () -> Unit) {
|
||||
fun RenderRelay(
|
||||
relay: RelayBriefInfoCache.RelayBriefInfo,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val relayInfo by
|
||||
produceState(
|
||||
|
@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@ -51,7 +52,7 @@ fun ReplyInformationChannel(
|
||||
replyTo: ImmutableList<Note>?,
|
||||
mentions: ImmutableList<String>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var sortedMentions by remember { mutableStateOf<ImmutableList<User>>(persistentListOf()) }
|
||||
|
||||
@ -67,7 +68,7 @@ fun ReplyInformationChannel(
|
||||
ReplyInformationChannel(
|
||||
replyTo,
|
||||
sortedMentions,
|
||||
onUserTagClick = { nav("User/${it.pubkeyHex}") },
|
||||
onUserTagClick = { nav.nav("User/${it.pubkeyHex}") },
|
||||
)
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ import com.vitorpamplona.amethyst.service.firstFullChar
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.types.RenderEmojiPack
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -145,7 +146,7 @@ class UpdateReactionTypeViewModel(
|
||||
fun UpdateReactionTypeDialog(
|
||||
onClose: () -> Unit,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val postViewModel: UpdateReactionTypeViewModel =
|
||||
viewModel(
|
||||
@ -326,7 +327,7 @@ private fun RenderReactionOption(
|
||||
@Composable
|
||||
private fun EmojiSelector(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
onClick: ((EmojiUrl) -> Unit)? = null,
|
||||
) {
|
||||
LoadAddressableNote(
|
||||
@ -361,7 +362,7 @@ private fun EmojiSelector(
|
||||
fun EmojiCollectionGallery(
|
||||
emojiCollections: ImmutableList<ATag>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
onClick: ((EmojiUrl) -> Unit)? = null,
|
||||
) {
|
||||
val color = MaterialTheme.colorScheme.background
|
||||
@ -385,14 +386,14 @@ private fun WatchAndRenderNote(
|
||||
emojiPack: AddressableNote,
|
||||
bgColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
onClick: ((EmojiUrl) -> Unit)?,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Column(
|
||||
Modifier.fillMaxWidth().clickable {
|
||||
scope.launch { routeFor(emojiPack, accountViewModel.userProfile())?.let { nav(it) } }
|
||||
scope.launch { routeFor(emojiPack, accountViewModel.userProfile())?.let { nav.nav(it) } }
|
||||
},
|
||||
) {
|
||||
RenderEmojiPack(
|
||||
|
@ -89,11 +89,11 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.AccountSettings
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.SaveButton
|
||||
import com.vitorpamplona.amethyst.ui.qrcode.SimpleQrCodeScanner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.getFragmentActivity
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.SimpleQrCodeScanner
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
|
@ -30,6 +30,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdPadding
|
||||
@ -39,12 +40,12 @@ fun UserCompose(
|
||||
baseUser: User,
|
||||
overallModifier: Modifier = StdPadding,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
overallModifier.clickable(
|
||||
onClick = { nav("User/${baseUser.pubkeyHex}") },
|
||||
onClick = { nav.nav("User/${baseUser.pubkeyHex}") },
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
|
@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.LoadUser
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@ -50,13 +51,13 @@ import com.vitorpamplona.quartz.events.ChatroomKey
|
||||
@Composable
|
||||
fun NoteAuthorPicture(
|
||||
baseNote: Note,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
size: Dp,
|
||||
pictureModifier: Modifier = Modifier,
|
||||
) {
|
||||
NoteAuthorPicture(baseNote, size, accountViewModel, pictureModifier) {
|
||||
nav("User/${it.pubkeyHex}")
|
||||
nav.nav("User/${it.pubkeyHex}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,7 +103,7 @@ fun UserPicture(
|
||||
size: Dp,
|
||||
pictureModifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadUser(baseUserHex = userHex, accountViewModel) {
|
||||
if (it != null) {
|
||||
@ -129,14 +130,14 @@ fun UserPicture(
|
||||
size: Dp,
|
||||
pictureModifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
ClickableUserPicture(
|
||||
baseUser = user,
|
||||
size = size,
|
||||
accountViewModel = accountViewModel,
|
||||
modifier = pictureModifier,
|
||||
onClick = { nav("User/${user.pubkeyHex}") },
|
||||
onClick = { nav.nav("User/${user.pubkeyHex}") },
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -45,13 +45,14 @@ import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.screen.ZapReqResponse
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.FollowButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ShowUserButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.UnfollowButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.WatchIsHiddenUser
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.showAmountAxis
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.FollowButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ShowUserButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.UnfollowButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.WatchIsHiddenUser
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ZapReqResponse
|
||||
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size55dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
@ -63,7 +64,7 @@ import kotlinx.coroutines.launch
|
||||
fun ZapNoteCompose(
|
||||
baseReqResponse: ZapReqResponse,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val baseNoteRequest by baseReqResponse.zapRequest
|
||||
.live()
|
||||
@ -86,7 +87,7 @@ fun ZapNoteCompose(
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.clickable(
|
||||
onClick = { nav(route) },
|
||||
onClick = { nav.nav(route) },
|
||||
),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
@ -99,7 +100,7 @@ fun ZapNoteCompose(
|
||||
private fun RenderZapNote(
|
||||
baseAuthor: User,
|
||||
zapNote: Note,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
accountViewModel: AccountViewModel,
|
||||
) {
|
||||
Row(
|
||||
|
@ -34,6 +34,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.ZapUserSetCard
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
@ -47,7 +48,7 @@ fun ZapUserSetCompose(
|
||||
isInnerNote: Boolean = false,
|
||||
routeForLastRead: String,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val backgroundColor =
|
||||
calculateBackgroundColor(
|
||||
@ -59,7 +60,7 @@ fun ZapUserSetCompose(
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.background(backgroundColor.value).clickable {
|
||||
nav("User/${zapSetCard.user.pubkeyHex}")
|
||||
nav.nav("User/${zapSetCard.user.pubkeyHex}")
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
|
@ -40,6 +40,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.AddDMRelayListDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
@ -58,7 +60,7 @@ fun AddInboxRelayForDMCardPreview() {
|
||||
ThemeComparisonColumn {
|
||||
AddInboxRelayForDMCard(
|
||||
accountViewModel = mockAccountViewModel(),
|
||||
nav = {},
|
||||
nav = EmptyNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -66,7 +68,7 @@ fun AddInboxRelayForDMCardPreview() {
|
||||
@Composable
|
||||
fun ObserveRelayListForDMsAndDisplayIfNotFound(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
ObserveRelayListForDMs(
|
||||
accountViewModel = accountViewModel,
|
||||
@ -115,7 +117,7 @@ fun ObserveRelayListForDMs(
|
||||
@Composable
|
||||
fun AddInboxRelayForDMCard(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(modifier = StdPadding) {
|
||||
Card(
|
||||
|
@ -40,6 +40,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.relays.AddSearchRelayListDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||
@ -58,7 +60,7 @@ fun AddInboxRelayForSearchCardPreview() {
|
||||
ThemeComparisonColumn {
|
||||
AddInboxRelayForSearchCard(
|
||||
accountViewModel = mockAccountViewModel(),
|
||||
nav = {},
|
||||
nav = EmptyNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -66,7 +68,7 @@ fun AddInboxRelayForSearchCardPreview() {
|
||||
@Composable
|
||||
fun ObserveRelayListForSearchAndDisplayIfNotFound(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
ObserveRelayListForSearch(
|
||||
accountViewModel = accountViewModel,
|
||||
@ -115,7 +117,7 @@ fun ObserveRelayListForSearch(
|
||||
@Composable
|
||||
fun AddInboxRelayForSearchCard(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(modifier = StdPadding) {
|
||||
Card(
|
||||
|
@ -30,6 +30,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
@ -39,7 +40,7 @@ import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||
fun DisplayFollowingCommunityInPost(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Column(HalfStartPadding) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) { DisplayCommunity(baseNote, nav) }
|
||||
@ -49,7 +50,7 @@ fun DisplayFollowingCommunityInPost(
|
||||
@Composable
|
||||
private fun DisplayCommunity(
|
||||
note: Note,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val communityTag =
|
||||
remember(note) { note.event?.getTagOfAddressableKind(CommunityDefinitionEvent.KIND) } ?: return
|
||||
@ -59,7 +60,7 @@ private fun DisplayCommunity(
|
||||
|
||||
ClickableText(
|
||||
text = displayTag,
|
||||
onClick = { nav(route) },
|
||||
onClick = { nav.nav(route) },
|
||||
style =
|
||||
LocalTextStyle.current.copy(
|
||||
color =
|
||||
|
@ -36,13 +36,14 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
|
||||
@Composable
|
||||
fun DisplayFollowingHashtagsInPost(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val userFollowState by accountViewModel.account.liveKind3Follows.collectAsStateWithLifecycle()
|
||||
var firstTag by remember(baseNote) { mutableStateOf<String?>(null) }
|
||||
@ -65,14 +66,14 @@ fun DisplayFollowingHashtagsInPost(
|
||||
@Composable
|
||||
private fun DisplayTagList(
|
||||
firstTag: String,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val displayTag = remember(firstTag) { AnnotatedString(" #$firstTag") }
|
||||
val route = remember(firstTag) { "Hashtag/$firstTag" }
|
||||
|
||||
ClickableText(
|
||||
text = displayTag,
|
||||
onClick = { nav(route) },
|
||||
onClick = { nav.nav(route) },
|
||||
style =
|
||||
LocalTextStyle.current.copy(
|
||||
color =
|
||||
|
@ -26,18 +26,19 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadCityName
|
||||
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
||||
|
||||
@Composable
|
||||
fun DisplayLocation(
|
||||
geohashStr: String,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
LoadCityName(geohashStr) { cityName ->
|
||||
ClickableText(
|
||||
text = AnnotatedString(cityName),
|
||||
onClick = { nav("Geohash/$geohashStr") },
|
||||
onClick = { nav.nav("Geohash/$geohashStr") },
|
||||
style =
|
||||
LocalTextStyle.current.copy(
|
||||
color =
|
||||
|
@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.actions.CloseButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.PostButton
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.ZappedIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||
@ -83,7 +84,7 @@ fun DisplayReward(
|
||||
baseReward: Reward,
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var popupExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
@ -94,7 +95,7 @@ fun DisplayReward(
|
||||
) {
|
||||
ClickableText(
|
||||
text = AnnotatedString("#bounty"),
|
||||
onClick = { nav("Hashtag/bounty") },
|
||||
onClick = { nav.nav("Hashtag/bounty") },
|
||||
style =
|
||||
LocalTextStyle.current.copy(
|
||||
color =
|
||||
|
@ -32,6 +32,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.vitorpamplona.amethyst.commons.richtext.HashTagSegment
|
||||
import com.vitorpamplona.amethyst.service.CachedRichTextParser
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.theme.HalfTopPadding
|
||||
import com.vitorpamplona.amethyst.ui.theme.lessImportantLink
|
||||
import com.vitorpamplona.quartz.events.Event
|
||||
@ -43,7 +44,7 @@ import kotlinx.coroutines.launch
|
||||
fun DisplayUncitedHashtags(
|
||||
event: Event,
|
||||
callbackUri: String? = null,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
DisplayUncitedHashtags(event, event.content, callbackUri, nav)
|
||||
}
|
||||
@ -54,7 +55,7 @@ fun DisplayUncitedHashtags(
|
||||
event: Event,
|
||||
content: String,
|
||||
callbackUri: String? = null,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val unusedHashtags by
|
||||
produceState(initialValue = emptyList<String>()) {
|
||||
@ -102,7 +103,7 @@ fun DisplayUncitedHashtags(
|
||||
unusedHashtags.forEach { hashtag ->
|
||||
ClickableText(
|
||||
text = remember { AnnotatedString("#$hashtag ") },
|
||||
onClick = { nav("Hashtag/$hashtag") },
|
||||
onClick = { nav.nav("Hashtag/$hashtag") },
|
||||
style = style,
|
||||
)
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.UserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.ZapSplitIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -46,7 +47,7 @@ fun DisplayZapSplits(
|
||||
noteEvent: EventInterface,
|
||||
useAuthorIfEmpty: Boolean = false,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val list =
|
||||
remember(noteEvent) {
|
||||
|
@ -46,6 +46,7 @@ import com.vitorpamplona.amethyst.ui.actions.EditPostView
|
||||
import com.vitorpamplona.amethyst.ui.actions.NewPostView
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.VerticalDotsIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.externalLinkForNote
|
||||
import com.vitorpamplona.amethyst.ui.note.types.EditState
|
||||
@ -63,7 +64,7 @@ fun MoreOptionsButton(
|
||||
baseNote: Note,
|
||||
editState: State<GenericLoadable<EditState>>? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val popupExpanded = remember { mutableStateOf(false) }
|
||||
|
||||
@ -101,7 +102,7 @@ fun NoteDropDownMenu(
|
||||
onDismiss: () -> Unit,
|
||||
editState: State<GenericLoadable<EditState>>? = null,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var reportDialogShowing by remember { mutableStateOf(false) }
|
||||
|
||||
|
@ -37,6 +37,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateClickableTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -51,7 +52,7 @@ fun ShowForkInformation(
|
||||
noteEvent: BaseTextNoteEvent,
|
||||
modifier: Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val forkedAddress = remember(noteEvent) { noteEvent.forkFromAddress() }
|
||||
val forkedEvent = remember(noteEvent) { noteEvent.forkFromVersion() }
|
||||
@ -78,7 +79,7 @@ fun ForkInformationRowLightColor(
|
||||
originalVersion: Note,
|
||||
modifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by originalVersion.live().metadata.observeAsState()
|
||||
val note = noteState?.note ?: return
|
||||
@ -93,7 +94,7 @@ fun ForkInformationRowLightColor(
|
||||
append(stringRes(id = R.string.forked_from))
|
||||
append(" ")
|
||||
},
|
||||
onClick = { nav(route) },
|
||||
onClick = { nav.nav(route) },
|
||||
style =
|
||||
LocalTextStyle.current.copy(
|
||||
color = MaterialTheme.colorScheme.nip05,
|
||||
@ -128,7 +129,7 @@ fun ForkInformationRow(
|
||||
originalVersion: Note,
|
||||
modifier: Modifier = Modifier,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by originalVersion.live().metadata.observeAsState()
|
||||
val note = noteState?.note ?: return
|
||||
|
@ -66,6 +66,8 @@ import com.vitorpamplona.amethyst.service.ZapPaymentHandler
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableText
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
|
||||
import com.vitorpamplona.amethyst.ui.note.CloseIcon
|
||||
@ -171,7 +173,7 @@ fun ZapTheDevsCardPreview() {
|
||||
ZapTheDevsCard(
|
||||
releaseNote,
|
||||
accountViewModel,
|
||||
nav = {},
|
||||
nav = EmptyNav,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -182,7 +184,7 @@ fun ZapTheDevsCardPreview() {
|
||||
fun ZapTheDevsCard(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val releaseNoteState by baseNote.live().metadata.observeAsState()
|
||||
val releaseNote = releaseNoteState?.note ?: return
|
||||
@ -227,7 +229,7 @@ fun ZapTheDevsCard(
|
||||
append("#value4value")
|
||||
}
|
||||
},
|
||||
onClick = { nav("Hashtag/value4value") },
|
||||
onClick = { nav.nav("Hashtag/value4value") },
|
||||
)
|
||||
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
@ -248,7 +250,7 @@ fun ZapTheDevsCard(
|
||||
}
|
||||
append(" " + stringRes(id = R.string.brought_to_you_by))
|
||||
},
|
||||
onClick = { nav(route) },
|
||||
onClick = { nav.nav(route) },
|
||||
)
|
||||
} else {
|
||||
Text(stringRes(id = R.string.this_version_brought_to_you_by))
|
||||
@ -285,7 +287,7 @@ fun ZapDonationButton(
|
||||
iconSize: Dp = Size35dp,
|
||||
iconSizeModifier: Modifier = Size20Modifier,
|
||||
animationSize: Dp = 14.dp,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var wantsToZap by remember { mutableStateOf<List<Long>?>(null) }
|
||||
var showErrorMessageDialog by remember { mutableStateOf<String?>(null) }
|
||||
@ -356,7 +358,7 @@ fun ZapDonationButton(
|
||||
baseNote.author?.let {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val route = routeToMessage(it, showErrorMessageDialog, accountViewModel)
|
||||
nav(route)
|
||||
nav.nav(route)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.components.ZoomableImageDialog
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.LinkIcon
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@ -81,7 +82,7 @@ import kotlinx.coroutines.withContext
|
||||
fun RenderAppDefinition(
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? AppDefinitionEvent ?: return
|
||||
|
||||
|
@ -45,6 +45,7 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadThumbAndThenVideoView
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.components.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||
@ -62,7 +63,7 @@ fun RenderAudioTrack(
|
||||
note: Note,
|
||||
isFiniteHeight: Boolean,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? AudioTrackEvent ?: return
|
||||
|
||||
@ -75,7 +76,7 @@ fun AudioTrackHeader(
|
||||
note: Note,
|
||||
isFiniteHeight: Boolean,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val media = remember { noteEvent.media() }
|
||||
val cover = remember { noteEvent.cover() }
|
||||
@ -118,7 +119,7 @@ fun AudioTrackHeader(
|
||||
Modifier
|
||||
.padding(top = 5.dp, start = 10.dp, end = 10.dp)
|
||||
.clickable {
|
||||
nav("User/${it.second.pubkeyHex}")
|
||||
nav.nav("User/${it.second.pubkeyHex}")
|
||||
},
|
||||
) {
|
||||
ClickableUserPicture(it.second, 25.dp, accountViewModel)
|
||||
@ -173,7 +174,7 @@ fun RenderAudioHeader(
|
||||
note: Note,
|
||||
isFiniteHeight: Boolean,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? AudioHeaderEvent ?: return
|
||||
|
||||
@ -186,7 +187,7 @@ fun AudioHeader(
|
||||
note: Note,
|
||||
isFiniteHeight: Boolean,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val media = remember { noteEvent.stream() ?: noteEvent.download() }
|
||||
val waveform = remember { noteEvent.wavefrom()?.toImmutableList()?.ifEmpty { null } }
|
||||
|
@ -63,6 +63,7 @@ import coil.request.SuccessResult
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@ -211,7 +212,7 @@ fun RenderBadgeAward(
|
||||
note: Note,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
if (note.replyTo.isNullOrEmpty()) return
|
||||
|
||||
@ -228,7 +229,7 @@ fun RenderBadgeAward(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(size = Size35dp)
|
||||
.clickable { nav("User/${user.pubkeyHex}") },
|
||||
.clickable { nav.nav("User/${user.pubkeyHex}") },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ClickableUserPicture(
|
||||
|
@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChannelHeader
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@ -46,7 +47,7 @@ fun RenderChannelMessage(
|
||||
backgroundColor: MutableState<Color>,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event
|
||||
val showChannelInfo =
|
||||
|
@ -33,6 +33,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChatroomHeader
|
||||
@ -49,7 +50,7 @@ fun RenderChatMessage(
|
||||
backgroundColor: MutableState<Color>,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val userRoom by
|
||||
remember(note) {
|
||||
@ -62,7 +63,7 @@ fun RenderChatMessage(
|
||||
if (it.users.size > 1 || (it.users.size == 1 && note.author == accountViewModel.account.userProfile())) {
|
||||
ChatroomHeader(it, MaterialTheme.colorScheme.replyModifier.padding(10.dp), accountViewModel) {
|
||||
routeFor(note, accountViewModel.userProfile())?.let {
|
||||
nav(it)
|
||||
nav.nav(it)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
@ -41,6 +41,7 @@ import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader
|
||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -56,7 +57,7 @@ fun RenderClassifieds(
|
||||
noteEvent: ClassifiedsEvent,
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val image = remember(noteEvent) { noteEvent.image() }
|
||||
val title = remember(noteEvent) { noteEvent.title() }
|
||||
|
@ -50,6 +50,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.LikeReaction
|
||||
@ -85,13 +86,13 @@ import java.util.Locale
|
||||
fun RenderCommunity(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
if (baseNote is AddressableNote) {
|
||||
Row(
|
||||
MaterialTheme.colorScheme.innerPostModifier
|
||||
.clickable {
|
||||
routeFor(baseNote, accountViewModel.userProfile())?.let { nav(it) }
|
||||
routeFor(baseNote, accountViewModel.userProfile())?.let { nav.nav(it) }
|
||||
}.padding(Size10dp),
|
||||
) {
|
||||
ShortCommunityHeader(
|
||||
@ -108,7 +109,7 @@ fun LongCommunityHeader(
|
||||
baseNote: AddressableNote,
|
||||
lineModifier: Modifier = Modifier.padding(horizontal = Size10dp, vertical = Size5dp),
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by baseNote.live().metadata.observeAsState()
|
||||
val noteEvent =
|
||||
@ -214,7 +215,7 @@ fun LongCommunityHeader(
|
||||
|
||||
participantUsers.forEach {
|
||||
Row(
|
||||
lineModifier.clickable { nav("User/${it.second.pubkeyHex}") },
|
||||
lineModifier.clickable { nav.nav("User/${it.second.pubkeyHex}") },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
it.first.role?.let { it1 ->
|
||||
@ -252,7 +253,7 @@ fun LongCommunityHeader(
|
||||
fun ShortCommunityHeader(
|
||||
baseNote: AddressableNote,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by baseNote.live().metadata.observeAsState()
|
||||
val noteEvent =
|
||||
@ -304,7 +305,7 @@ fun ShortCommunityHeader(
|
||||
private fun ShortCommunityActionOptions(
|
||||
note: AddressableNote,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Spacer(modifier = StdHorzSpacer)
|
||||
Row(
|
||||
@ -337,7 +338,7 @@ private fun ShortCommunityActionOptions(
|
||||
private fun LongCommunityActionOptions(
|
||||
note: AddressableNote,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
WatchAddressableNoteFollows(note, accountViewModel) { isFollowing ->
|
||||
if (isFollowing) {
|
||||
|
@ -46,6 +46,7 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableUrl
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadDecryptedContent
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||
@ -75,7 +76,7 @@ fun RenderGitPatchEvent(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val event = baseNote.event as? GitPatchEvent ?: return
|
||||
|
||||
@ -95,7 +96,7 @@ fun RenderGitPatchEvent(
|
||||
private fun RenderShortRepositoryHeader(
|
||||
baseNote: AddressableNote,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by baseNote.live().metadata.observeAsState()
|
||||
val noteEvent = noteState?.note?.event as? GitRepositoryEvent ?: return
|
||||
@ -132,7 +133,7 @@ private fun RenderGitPatchEvent(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val repository = remember(noteEvent) { noteEvent.repository() }
|
||||
|
||||
@ -212,7 +213,7 @@ fun RenderGitIssueEvent(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val event = baseNote.event as? GitIssueEvent ?: return
|
||||
|
||||
@ -237,7 +238,7 @@ private fun RenderGitIssueEvent(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val repository = remember(noteEvent) { noteEvent.repository() }
|
||||
|
||||
@ -309,7 +310,7 @@ private fun RenderGitIssueEvent(
|
||||
fun RenderGitRepositoryEvent(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val event = baseNote.event as? GitRepositoryEvent ?: return
|
||||
|
||||
@ -321,7 +322,7 @@ private fun RenderGitRepositoryEvent(
|
||||
noteEvent: GitRepositoryEvent,
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val title = noteEvent.name() ?: noteEvent.dTag()
|
||||
val summary = noteEvent.description()
|
||||
|
@ -50,6 +50,7 @@ import com.vitorpamplona.amethyst.ui.components.DisplayEvent
|
||||
import com.vitorpamplona.amethyst.ui.components.RenderUserAsClickableText
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.components.measureSpaceWidth
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.encoders.ATag
|
||||
@ -69,7 +70,7 @@ fun RenderHighlight(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? HighlightEvent ?: return
|
||||
|
||||
@ -103,7 +104,7 @@ fun DisplayHighlight(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val quote =
|
||||
remember {
|
||||
@ -149,7 +150,7 @@ private fun DisplayQuoteAuthor(
|
||||
postAddress: ATag?,
|
||||
postVersion: HexKey?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
var userBase by remember { mutableStateOf<User?>(authorHex?.let { accountViewModel.getUserIfExists(it) }) }
|
||||
|
||||
@ -216,7 +217,7 @@ private fun DisplayQuoteAuthor(
|
||||
fun DisplayEntryForUser(
|
||||
userBase: User,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val userMetadata by userBase.live().userMetadataInfo.observeAsState()
|
||||
|
||||
@ -234,7 +235,7 @@ fun DisplayEntryForNote(
|
||||
note: Note,
|
||||
userBase: User?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteState by note.live().metadata.observeAsState()
|
||||
|
||||
@ -253,7 +254,7 @@ fun DisplayEntryForNote(
|
||||
if (description != null) {
|
||||
ClickableText(
|
||||
text = AnnotatedString(description),
|
||||
onClick = { routeFor(note, accountViewModel.userProfile())?.let { nav(it) } },
|
||||
onClick = { routeFor(note, accountViewModel.userProfile())?.let { nav.nav(it) } },
|
||||
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary),
|
||||
)
|
||||
} else {
|
||||
@ -266,7 +267,7 @@ fun DisplayEntryForAUrl(
|
||||
url: String,
|
||||
userBase: User?,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
if (userBase != null) {
|
||||
DisplayEntryForUser(userBase, accountViewModel, nav)
|
||||
|
@ -51,6 +51,8 @@ import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
|
||||
import com.vitorpamplona.amethyst.ui.components.VideoView
|
||||
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.note.DisplayAuthorBanner
|
||||
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
|
||||
@ -82,7 +84,7 @@ import java.util.Locale
|
||||
fun RenderLiveActivityEventPreview() {
|
||||
val event = Event.fromJson("{\"id\":\"19406ad34ce3c653d62eb73c1816ac27dcf473c2ccdccf5af7d90d2633c62561\",\"pubkey\":\"6b66886b3add72c779d205be574ec2d7cec619061ac3e75717389b26445989e4\",\"created_at\":1719084750,\"kind\":30311,\"tags\":[[\"r\",\"podcast:guid:72d5e069-f907-5ee7-b0d7-45404f4f0aa5\"],[\"r\",\"podcast:item:guid:bfc33d6e-e00f-4f11-a2ff-94865b7867aa\"],[\"d\",\"bfc33d6e-e00f-4f11-a2ff-94865b7867aa\"],[\"title\",\"The Online Identity Time Bomb\"],[\"summary\",\"Online identity is a ticking time bomb. But are trustworthy, open-source solutions ready to disarm it, or will we be stuck with lackluster, proprietary systems?\\n Live chat: https:/jblive.tv\"],[\"streaming\",\"https://jblive.fm\"],[\"starts\",\"1719167400\"],[\"status\",\"planned\"],[\"image\",\"https://station.us-iad-1.linodeobjects.com/art/lup-mp3.jpg\"]],\"content\":\"\",\"sig\":\"2ce3fae9ad4512541aaae4dbd9484f50df62ab95ba935d7512b736f087f151c1d15ec2bf62d3135474f16c3e070f335b24349c5493461873ddca3051804ca944\"}") as LiveActivitiesEvent
|
||||
val accountViewModel = mockAccountViewModel()
|
||||
val nav: (String) -> Unit = {}
|
||||
val nav = EmptyNav
|
||||
val baseNote: Note?
|
||||
|
||||
runBlocking {
|
||||
@ -105,7 +107,7 @@ fun RenderLiveActivityEventPreview() {
|
||||
fun RenderLiveActivityEvent(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Row(modifier = Modifier.padding(top = 5.dp)) {
|
||||
Column(
|
||||
@ -121,7 +123,7 @@ fun RenderLiveActivityEvent(
|
||||
fun RenderLiveActivityEventInner(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? LiveActivitiesEvent ?: return
|
||||
|
||||
@ -249,7 +251,7 @@ fun RenderLiveActivityEventInner(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
.clickable { nav("User/${it.second.pubkeyHex}") },
|
||||
.clickable { nav.nav("User/${it.second.pubkeyHex}") },
|
||||
) {
|
||||
ClickableUserPicture(it.second, 25.dp, accountViewModel)
|
||||
Spacer(StdHorzSpacer)
|
||||
|
@ -31,6 +31,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ChannelHeader
|
||||
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||
@ -46,7 +47,7 @@ fun RenderLiveActivityChatMessage(
|
||||
backgroundColor: MutableState<Color>,
|
||||
editState: State<GenericLoadable<EditState>>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event
|
||||
val showChannelInfo =
|
||||
|
@ -38,6 +38,7 @@ import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeader
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
@ -51,7 +52,7 @@ import com.vitorpamplona.quartz.events.LongTextNoteEvent
|
||||
fun RenderLongFormContent(
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? LongTextNoteEvent ?: return
|
||||
|
||||
|
@ -49,6 +49,7 @@ import com.vitorpamplona.amethyst.model.Resource
|
||||
import com.vitorpamplona.amethyst.model.VisionPrescription
|
||||
import com.vitorpamplona.amethyst.model.findReferenceInDb
|
||||
import com.vitorpamplona.amethyst.model.parseResourceBundleOrNull
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||
@ -77,7 +78,7 @@ fun RenderEyeGlassesPrescriptionPreview() {
|
||||
fun RenderFhirResource(
|
||||
baseNote: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val event = baseNote.event as? FhirResourceEvent ?: return
|
||||
|
||||
|
@ -30,6 +30,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.events.EmptyTagList
|
||||
@ -44,7 +45,7 @@ fun RenderNIP90ContentDiscoveryResponse(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? NIP90ContentDiscoveryResponseEvent ?: return
|
||||
val callbackUri = remember(note) { note.toNostrUri() }
|
||||
|
@ -24,6 +24,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.quartz.events.NIP90StatusEvent
|
||||
|
||||
@ -31,7 +32,7 @@ import com.vitorpamplona.quartz.events.NIP90StatusEvent
|
||||
fun RenderNIP90Status(
|
||||
note: Note,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? NIP90StatusEvent ?: return
|
||||
|
||||
|
@ -49,6 +49,7 @@ import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.ShowMoreButton
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.UserCompose
|
||||
import com.vitorpamplona.amethyst.ui.note.getGradient
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -63,7 +64,7 @@ fun DisplayPeopleList(
|
||||
baseNote: Note,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? PeopleListEvent ?: return
|
||||
|
||||
|
@ -48,6 +48,7 @@ import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.ShowMoreButton
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.PinIcon
|
||||
import com.vitorpamplona.amethyst.ui.note.getGradient
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@ -61,7 +62,7 @@ fun RenderPinListEvent(
|
||||
baseNote: Note,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = baseNote.event as? PinListEvent ?: return
|
||||
|
||||
|
@ -35,6 +35,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.PollNote
|
||||
import com.vitorpamplona.amethyst.ui.note.ReplyNoteComposition
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||
@ -55,7 +56,7 @@ fun RenderPoll(
|
||||
unPackReply: Boolean,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? PollNoteEvent ?: return
|
||||
val eventContent = noteEvent.content()
|
||||
|
@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
|
||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routeFor
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadDecryptedContent
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||
@ -61,7 +62,7 @@ fun RenderPrivateMessage(
|
||||
quotesLeft: Int,
|
||||
backgroundColor: MutableState<Color>,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
val noteEvent = note.event as? PrivateDmEvent ?: return
|
||||
|
||||
@ -76,7 +77,7 @@ fun RenderPrivateMessage(
|
||||
if (it.users.size > 1 || (it.users.size == 1 && note.author == accountViewModel.account.userProfile())) {
|
||||
ChatroomHeader(it, MaterialTheme.colorScheme.replyModifier.padding(10.dp), accountViewModel) {
|
||||
routeFor(note, accountViewModel.userProfile())?.let {
|
||||
nav(it)
|
||||
nav.nav(it)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = StdVertSpacer)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user