mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-07-22 07:48:27 +00:00
Compare commits
3 Commits
c79b795477
...
claude/res
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fb01c1e46 | ||
|
|
c3378efffb | ||
|
|
0cbd8005d9 |
@@ -257,6 +257,10 @@ dependencies {
|
||||
// Adaptive Layout / Two Pane
|
||||
implementation libs.androidx.material3.windowSize
|
||||
implementation libs.accompanist.adaptive
|
||||
implementation libs.androidx.material3.adaptive
|
||||
implementation libs.androidx.material3.adaptive.layout
|
||||
implementation libs.androidx.material3.adaptive.navigation
|
||||
implementation libs.androidx.material3.adaptive.navigation.suite
|
||||
|
||||
// Lifecycle
|
||||
implementation libs.androidx.lifecycle.runtime.ktx
|
||||
|
||||
@@ -58,6 +58,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectSingleFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -101,241 +102,243 @@ fun NewUserMetadataScreen(
|
||||
)
|
||||
},
|
||||
) { pad ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
start = 10.dp,
|
||||
end = 10.dp,
|
||||
top = pad.calculateTopPadding(),
|
||||
bottom = pad.calculateBottomPadding(),
|
||||
).consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
) {
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
Column(
|
||||
modifier = Modifier.padding(10.dp).verticalScroll(rememberScrollState()),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(
|
||||
start = 10.dp,
|
||||
end = 10.dp,
|
||||
top = pad.calculateTopPadding(),
|
||||
bottom = pad.calculateBottomPadding(),
|
||||
).consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
) {
|
||||
// -- Profile (always visible) --
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.username)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.name.value,
|
||||
onValueChange = { postViewModel.name.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(R.string.my_name),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
prefix = { Text("@") },
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.display_name)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.displayName.value,
|
||||
onValueChange = { postViewModel.displayName.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(R.string.my_display_name),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.about_me)) },
|
||||
modifier = Modifier.fillMaxWidth().height(100.dp),
|
||||
value = postViewModel.about.value,
|
||||
onValueChange = { postViewModel.about.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(id = R.string.about_me),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
maxLines = 10,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.avatar_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.picture.value,
|
||||
onValueChange = { postViewModel.picture.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "https://mywebsite.com/me.jpg",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
leadingIcon = {
|
||||
SelectSingleFromGallery(
|
||||
isUploading = postViewModel.isUploadingImageForPicture,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(start = 5.dp),
|
||||
) {
|
||||
postViewModel.uploadForPicture(it, context, onError = accountViewModel.toastManager::toast)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.banner_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.banner.value,
|
||||
onValueChange = { postViewModel.banner.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "https://mywebsite.com/mybanner.jpg",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
leadingIcon = {
|
||||
SelectSingleFromGallery(
|
||||
isUploading = postViewModel.isUploadingImageForBanner,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(start = 5.dp),
|
||||
) {
|
||||
postViewModel.uploadForBanner(it, context, onError = accountViewModel.toastManager::toast)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.lightning_address)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.lnAddress.value,
|
||||
onValueChange = { postViewModel.lnAddress.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "me@mylightningnode.com",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.nip_05) + " (NIP-05)") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.nip05.value,
|
||||
onValueChange = { postViewModel.nip05.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "_@mywebsite.com",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.website_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.website.value,
|
||||
onValueChange = { postViewModel.website.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "https://mywebsite.com",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.pronouns)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.pronouns.value,
|
||||
onValueChange = { postViewModel.pronouns.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "they/them, ...",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
// -- Social Proofs --
|
||||
ExpandableSection(
|
||||
title = stringRes(R.string.social_proof),
|
||||
expanded = socialExpanded,
|
||||
Column(
|
||||
modifier = Modifier.padding(10.dp).verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
// -- Profile (always visible) --
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.twitter)) },
|
||||
label = { Text(text = stringRes(R.string.username)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.twitter.value,
|
||||
onValueChange = { postViewModel.twitter.value = it },
|
||||
value = postViewModel.name.value,
|
||||
onValueChange = { postViewModel.name.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(R.string.twitter_proof_url_template),
|
||||
text = stringRes(R.string.my_name),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
prefix = { Text("@") },
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.mastodon)) },
|
||||
label = { Text(text = stringRes(R.string.display_name)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.mastodon.value,
|
||||
onValueChange = { postViewModel.mastodon.value = it },
|
||||
value = postViewModel.displayName.value,
|
||||
onValueChange = { postViewModel.displayName.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(R.string.mastodon_proof_url_template),
|
||||
text = stringRes(R.string.my_display_name),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.github)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.github.value,
|
||||
onValueChange = { postViewModel.github.value = it },
|
||||
label = { Text(text = stringRes(R.string.about_me)) },
|
||||
modifier = Modifier.fillMaxWidth().height(100.dp),
|
||||
value = postViewModel.about.value,
|
||||
onValueChange = { postViewModel.about.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(R.string.github_proof_url_template),
|
||||
text = stringRes(id = R.string.about_me),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
keyboardOptions =
|
||||
KeyboardOptions.Default.copy(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
),
|
||||
maxLines = 10,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.avatar_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.picture.value,
|
||||
onValueChange = { postViewModel.picture.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "https://mywebsite.com/me.jpg",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
leadingIcon = {
|
||||
SelectSingleFromGallery(
|
||||
isUploading = postViewModel.isUploadingImageForPicture,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(start = 5.dp),
|
||||
) {
|
||||
postViewModel.uploadForPicture(it, context, onError = accountViewModel.toastManager::toast)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.banner_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.banner.value,
|
||||
onValueChange = { postViewModel.banner.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "https://mywebsite.com/mybanner.jpg",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
leadingIcon = {
|
||||
SelectSingleFromGallery(
|
||||
isUploading = postViewModel.isUploadingImageForBanner,
|
||||
tint = MaterialTheme.colorScheme.placeholderText,
|
||||
modifier = Modifier.padding(start = 5.dp),
|
||||
) {
|
||||
postViewModel.uploadForBanner(it, context, onError = accountViewModel.toastManager::toast)
|
||||
}
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.lightning_address)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.lnAddress.value,
|
||||
onValueChange = { postViewModel.lnAddress.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "me@mylightningnode.com",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.nip_05) + " (NIP-05)") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.nip05.value,
|
||||
onValueChange = { postViewModel.nip05.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "_@mywebsite.com",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.website_url)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.website.value,
|
||||
onValueChange = { postViewModel.website.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "https://mywebsite.com",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.pronouns)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.pronouns.value,
|
||||
onValueChange = { postViewModel.pronouns.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "they/them, ...",
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
singleLine = true,
|
||||
)
|
||||
|
||||
// -- Social Proofs --
|
||||
ExpandableSection(
|
||||
title = stringRes(R.string.social_proof),
|
||||
expanded = socialExpanded,
|
||||
) {
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.twitter)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.twitter.value,
|
||||
onValueChange = { postViewModel.twitter.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(R.string.twitter_proof_url_template),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.mastodon)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.mastodon.value,
|
||||
onValueChange = { postViewModel.mastodon.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(R.string.mastodon_proof_url_template),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
label = { Text(text = stringRes(R.string.github)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
value = postViewModel.github.value,
|
||||
onValueChange = { postViewModel.github.value = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringRes(R.string.github_proof_url_template),
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -83,29 +84,31 @@ fun MediaServersScaffold(
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
top = padding.calculateTopPadding(),
|
||||
end = 16.dp,
|
||||
bottom = padding.calculateBottomPadding(),
|
||||
).consumeWindowInsets(padding)
|
||||
.imePadding(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp, alignment = Alignment.Top),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(id = R.string.set_preferred_media_servers),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 10.dp),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
top = padding.calculateTopPadding(),
|
||||
end = 16.dp,
|
||||
bottom = padding.calculateBottomPadding(),
|
||||
).consumeWindowInsets(padding)
|
||||
.imePadding(),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp, alignment = Alignment.Top),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(id = R.string.set_preferred_media_servers),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 10.dp),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
)
|
||||
|
||||
AllMediaBody(blossomServersViewModel)
|
||||
AllMediaBody(blossomServersViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.layouts
|
||||
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.window.core.layout.WindowWidthSizeClass
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.BottomBarRoute
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.bottomNavigationItems
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.painterRes
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
|
||||
val LocalWindowSizeClass = compositionLocalOf { WindowWidthSizeClass.COMPACT }
|
||||
|
||||
@Composable
|
||||
fun isCompactWindow(): Boolean = LocalWindowSizeClass.current == WindowWidthSizeClass.COMPACT
|
||||
|
||||
@Composable
|
||||
fun AdaptiveScaffold(
|
||||
selectedRoute: Route?,
|
||||
onNavigate: (Route) -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val adaptiveInfo = currentWindowAdaptiveInfo()
|
||||
val windowSizeClass = adaptiveInfo.windowSizeClass.windowWidthSizeClass
|
||||
|
||||
CompositionLocalProvider(LocalWindowSizeClass provides windowSizeClass) {
|
||||
NavigationSuiteScaffold(
|
||||
navigationSuiteItems = {
|
||||
bottomNavigationItems.forEach { navItem ->
|
||||
val selected = navItem.route == selectedRoute
|
||||
item(
|
||||
selected = selected,
|
||||
onClick = { onNavigate(navItem.route) },
|
||||
icon = {
|
||||
AdaptiveNavIcon(navItem, selected)
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AdaptiveNavIcon(
|
||||
route: BottomBarRoute,
|
||||
selected: Boolean,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterRes(resourceId = route.icon, 0),
|
||||
contentDescription = stringRes(route.contentDescriptor),
|
||||
modifier = route.iconSize,
|
||||
tint = if (selected) MaterialTheme.colorScheme.primary else Color.Unspecified,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.layouts
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun MaxWidthContainer(
|
||||
maxWidth: Dp = 640.dp,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
Box(modifier = Modifier.widthIn(max = maxWidth).fillMaxHeight()) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,11 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.util.Consumer
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.NavDestination.Companion.hasRoute
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.crashreports.DisplayCrashMessages
|
||||
@@ -48,6 +51,7 @@ import com.vitorpamplona.amethyst.ui.actions.mediaServers.AllMediaServersScreen
|
||||
import com.vitorpamplona.amethyst.ui.broadcast.DisplayBroadcastProgress
|
||||
import com.vitorpamplona.amethyst.ui.components.getActivity
|
||||
import com.vitorpamplona.amethyst.ui.components.toasts.DisplayErrorMessages
|
||||
import com.vitorpamplona.amethyst.ui.layouts.AdaptiveScaffold
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.rememberNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
@@ -109,7 +113,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.relay.RelayFeedScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.AllRelayListScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.SearchScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.AllSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.AdaptiveSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NIP47SetupScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NamecoinSettingsScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.ReactionsSettingsScreen
|
||||
@@ -137,201 +141,212 @@ fun AppNavigation(
|
||||
) {
|
||||
val nav = rememberNav()
|
||||
|
||||
AccountSwitcherAndLeftDrawerLayout(accountViewModel, accountSessionManager, nav) {
|
||||
NavHost(
|
||||
navController = nav.controller,
|
||||
startDestination = Route.Home,
|
||||
enterTransition = { fadeIn(animationSpec = tween(200)) },
|
||||
exitTransition = { fadeOut(animationSpec = tween(200)) },
|
||||
) {
|
||||
composable<Route.Home> { HomeScreen(accountViewModel, nav) }
|
||||
composable<Route.Message> { MessagesScreen(accountViewModel, nav) }
|
||||
composable<Route.Video> { VideoScreen(accountViewModel, nav) }
|
||||
composable<Route.Discover> { DiscoverScreen(accountViewModel, nav) }
|
||||
composable<Route.Notification> { NotificationScreen(accountViewModel, nav) }
|
||||
composable<Route.Chess> { ChessLobbyScreen(accountViewModel, nav) }
|
||||
val currentBackStackEntry by nav.controller.currentBackStackEntryAsState()
|
||||
val selectedRoute =
|
||||
remember(currentBackStackEntry) {
|
||||
currentBackStackEntry?.toBaseRoute()
|
||||
}
|
||||
|
||||
composableFromEnd<Route.Lists> { ListOfPeopleListsScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.MyPeopleListView> { PeopleListScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.MyFollowPackView> { FollowPackScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.PeopleListManagement> { FollowListAndPackAndUserScreen(it.userToAdd, accountViewModel, nav) }
|
||||
AdaptiveScaffold(
|
||||
selectedRoute = selectedRoute,
|
||||
onNavigate = { route -> nav.newStack(route) },
|
||||
) {
|
||||
AccountSwitcherAndLeftDrawerLayout(accountViewModel, accountSessionManager, nav) {
|
||||
NavHost(
|
||||
navController = nav.controller,
|
||||
startDestination = Route.Home,
|
||||
enterTransition = { fadeIn(animationSpec = tween(200)) },
|
||||
exitTransition = { fadeOut(animationSpec = tween(200)) },
|
||||
) {
|
||||
composable<Route.Home> { HomeScreen(accountViewModel, nav) }
|
||||
composable<Route.Message> { MessagesScreen(accountViewModel, nav) }
|
||||
composable<Route.Video> { VideoScreen(accountViewModel, nav) }
|
||||
composable<Route.Discover> { DiscoverScreen(accountViewModel, nav) }
|
||||
composable<Route.Notification> { NotificationScreen(accountViewModel, nav) }
|
||||
composable<Route.Chess> { ChessLobbyScreen(accountViewModel, nav) }
|
||||
|
||||
composableFromBottomArgs<Route.PeopleListMetadataEdit> { PeopleListMetadataScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.FollowPackMetadataEdit> { FollowPackMetadataScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromEnd<Route.Lists> { ListOfPeopleListsScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.MyPeopleListView> { PeopleListScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.MyFollowPackView> { FollowPackScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.PeopleListManagement> { FollowListAndPackAndUserScreen(it.userToAdd, accountViewModel, nav) }
|
||||
|
||||
composableFromEnd<Route.BookmarkGroups> { ListOfBookmarkGroupsScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.BookmarkGroupView> { BookmarkGroupScreen(it.dTag, it.bookmarkType, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.BookmarkGroupMetadataEdit> { BookmarkGroupMetadataScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.PostBookmarkManagement> { PostBookmarkListManagementScreen(it.postId, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.ArticleBookmarkManagement> { ArticleBookmarkListManagementScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.PeopleListMetadataEdit> { PeopleListMetadataScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.FollowPackMetadataEdit> { FollowPackMetadataScreen(it.dTag, accountViewModel, nav) }
|
||||
|
||||
composableFromBottomArgs<Route.QRDisplay> { ShowQRScreen(it.pubkey, accountViewModel, nav) }
|
||||
composableFromEnd<Route.BookmarkGroups> { ListOfBookmarkGroupsScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.BookmarkGroupView> { BookmarkGroupScreen(it.dTag, it.bookmarkType, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.BookmarkGroupMetadataEdit> { BookmarkGroupMetadataScreen(it.dTag, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.PostBookmarkManagement> { PostBookmarkListManagementScreen(it.postId, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.ArticleBookmarkManagement> { ArticleBookmarkListManagementScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
||||
|
||||
composableFromBottomArgs<Route.ManualZapSplitPayment> { PayViaIntentScreen(it.paymentId, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.QRDisplay> { ShowQRScreen(it.pubkey, accountViewModel, nav) }
|
||||
|
||||
composableFromBottomArgs<Route.EditProfile> { NewUserMetadataScreen(nav, accountViewModel) }
|
||||
composable<Route.Search> { SearchScreen(accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.ManualZapSplitPayment> { PayViaIntentScreen(it.paymentId, accountViewModel, nav) }
|
||||
|
||||
composableFromEnd<Route.AllSettings> { AllSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.AccountBackup> { AccountBackupScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.SecurityFilters> { SecurityFiltersScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.PrivacyOptions> { PrivacyOptionsScreen(Amethyst.instance.torPrefs.value, nav) }
|
||||
composableFromEnd<Route.NamecoinSettings> { NamecoinSettingsScreen(Amethyst.instance.namecoinPrefs, nav) }
|
||||
composableFromEnd<Route.Bookmarks> { BookmarkListScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Drafts> { DraftListScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Settings> { SettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.UserSettings> { UserSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.ReactionsSettings> { ReactionsSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.ImportFollowsSelectUser> { ImportFollowListSelectUserScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.ImportFollowsPickFollows> {
|
||||
ImportFollowListPickFollowsScreen(
|
||||
accountViewModel.getOrCreateAddressableNote(ContactListEvent.createAddress(it.userHex)),
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
composableFromBottomArgs<Route.EditProfile> { NewUserMetadataScreen(nav, accountViewModel) }
|
||||
composable<Route.Search> { SearchScreen(accountViewModel, nav) }
|
||||
|
||||
composableFromEndArgs<Route.Nip47NWCSetup> { NIP47SetupScreen(accountViewModel, nav, it.nip47) }
|
||||
composableFromEndArgs<Route.UpdateZapAmount> { UpdateZapAmountScreen(accountViewModel, nav, it.nip47) }
|
||||
composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.EditMediaServers> { AllMediaServersScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.UpdateReactionType> { UpdateReactionTypeScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.AllSettings> { AdaptiveSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.AccountBackup> { AccountBackupScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.SecurityFilters> { SecurityFiltersScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.PrivacyOptions> { PrivacyOptionsScreen(Amethyst.instance.torPrefs.value, nav) }
|
||||
composableFromEnd<Route.NamecoinSettings> { NamecoinSettingsScreen(Amethyst.instance.namecoinPrefs, nav) }
|
||||
composableFromEnd<Route.Bookmarks> { BookmarkListScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Drafts> { DraftListScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.Settings> { SettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.UserSettings> { UserSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.ReactionsSettings> { ReactionsSettingsScreen(accountViewModel, nav) }
|
||||
composableFromEnd<Route.ImportFollowsSelectUser> { ImportFollowListSelectUserScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.ImportFollowsPickFollows> {
|
||||
ImportFollowListPickFollowsScreen(
|
||||
accountViewModel.getOrCreateAddressableNote(ContactListEvent.createAddress(it.userHex)),
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromEndArgs<Route.ContentDiscovery> { DvmContentDiscoveryScreen(it.id, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Profile> { ProfileScreen(it.id, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Note> { ThreadScreen(it.id, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Hashtag> { HashtagScreen(it, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Geohash> { GeoHashScreen(it, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.RelayFeed> { RelayFeedScreen(it, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.ChessGame> { ChessGameScreen(it.gameId, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.RelayInfo> { RelayInformationScreen(it.url, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Community> { CommunityScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.FollowPack> { FollowPackFeedScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Nip47NWCSetup> { NIP47SetupScreen(accountViewModel, nav, it.nip47) }
|
||||
composableFromEndArgs<Route.UpdateZapAmount> { UpdateZapAmountScreen(accountViewModel, nav, it.nip47) }
|
||||
composableFromEndArgs<Route.EditRelays> { AllRelayListScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.EditMediaServers> { AllMediaServersScreen(accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.UpdateReactionType> { UpdateReactionTypeScreen(accountViewModel, nav) }
|
||||
|
||||
composableFromEndArgs<Route.Room> { ChatroomScreen(it.toKey(), it.message, it.replyId, it.draftId, it.expiresDays, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.RoomByAuthor> { ChatroomByAuthorScreen(it.id, null, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.ContentDiscovery> { DvmContentDiscoveryScreen(it.id, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Profile> { ProfileScreen(it.id, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Note> { ThreadScreen(it.id, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Hashtag> { HashtagScreen(it, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Geohash> { GeoHashScreen(it, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.RelayFeed> { RelayFeedScreen(it, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.ChessGame> { ChessGameScreen(it.gameId, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.RelayInfo> { RelayInformationScreen(it.url, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.Community> { CommunityScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.FollowPack> { FollowPackFeedScreen(Address(it.kind, it.pubKeyHex, it.dTag), accountViewModel, nav) }
|
||||
|
||||
composableFromEndArgs<Route.PublicChatChannel> {
|
||||
PublicChatChannelScreen(
|
||||
it.id,
|
||||
it.draftId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
it.replyTo?.let { hex -> accountViewModel.checkGetOrCreateNote(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
composableFromEndArgs<Route.Room> { ChatroomScreen(it.toKey(), it.message, it.replyId, it.draftId, it.expiresDays, accountViewModel, nav) }
|
||||
composableFromEndArgs<Route.RoomByAuthor> { ChatroomByAuthorScreen(it.id, null, accountViewModel, nav) }
|
||||
|
||||
composableFromEndArgs<Route.LiveActivityChannel> {
|
||||
LiveActivityChannelScreen(
|
||||
Address(it.kind, it.pubKeyHex, it.dTag),
|
||||
draft = it.draftId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
replyTo = it.replyTo?.let { hex -> accountViewModel.checkGetOrCreateNote(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
composableFromEndArgs<Route.PublicChatChannel> {
|
||||
PublicChatChannelScreen(
|
||||
it.id,
|
||||
it.draftId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
it.replyTo?.let { hex -> accountViewModel.checkGetOrCreateNote(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromEndArgs<Route.EphemeralChat> {
|
||||
RelayUrlNormalizer.normalizeOrNull(it.relayUrl)?.let { relay ->
|
||||
EphemeralChatScreen(
|
||||
channelId = RoomId(it.id, relay),
|
||||
composableFromEndArgs<Route.LiveActivityChannel> {
|
||||
LiveActivityChannelScreen(
|
||||
Address(it.kind, it.pubKeyHex, it.dTag),
|
||||
draft = it.draftId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
replyTo = it.replyTo?.let { hex -> accountViewModel.checkGetOrCreateNote(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromEndArgs<Route.EphemeralChat> {
|
||||
RelayUrlNormalizer.normalizeOrNull(it.relayUrl)?.let { relay ->
|
||||
EphemeralChatScreen(
|
||||
channelId = RoomId(it.id, relay),
|
||||
draft = it.draftId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
replyTo = it.replyTo?.let { hex -> accountViewModel.checkGetOrCreateNote(hex) },
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.ChannelMetadataEdit> { ChannelMetadataScreen(it.id, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.NewEphemeralChat> { NewEphemeralChatScreen(accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.NewGroupDM> { NewGroupDMScreen(it.message, it.attachment, accountViewModel, nav) }
|
||||
|
||||
composableArgs<Route.EventRedirect> { LoadRedirectScreen(it.id, accountViewModel, nav) }
|
||||
|
||||
composableFromBottomArgs<Route.GeoPost> {
|
||||
GeoHashPostScreen(
|
||||
geohash = it.geohash,
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
reply = it.replyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.NewPublicMessage> {
|
||||
NewPublicMessageScreen(
|
||||
to = it.toKey(),
|
||||
reply = it.replyId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draftId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.ChannelMetadataEdit> { ChannelMetadataScreen(it.id, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.NewEphemeralChat> { NewEphemeralChatScreen(accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.NewGroupDM> { NewGroupDMScreen(it.message, it.attachment, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.HashtagPost> {
|
||||
HashtagPostScreen(
|
||||
hashtag = it.hashtag,
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
reply = it.replyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableArgs<Route.EventRedirect> { LoadRedirectScreen(it.id, accountViewModel, nav) }
|
||||
composableFromBottomArgs<Route.GenericCommentPost> {
|
||||
ReplyCommentPostScreen(
|
||||
reply = it.replyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.GeoPost> {
|
||||
GeoHashPostScreen(
|
||||
geohash = it.geohash,
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
reply = it.replyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
composableFromBottomArgs<Route.NewProduct> {
|
||||
NewProductScreen(
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.NewPublicMessage> {
|
||||
NewPublicMessageScreen(
|
||||
to = it.toKey(),
|
||||
reply = it.replyId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draftId?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
composableFromBottomArgs<Route.NewShortNote> {
|
||||
ShortNotePostScreen(
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
baseReplyTo = it.baseReplyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
fork = it.fork?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
version = it.version?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.HashtagPost> {
|
||||
HashtagPostScreen(
|
||||
hashtag = it.hashtag,
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
reply = it.replyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.GenericCommentPost> {
|
||||
ReplyCommentPostScreen(
|
||||
reply = it.replyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.NewProduct> {
|
||||
NewProductScreen(
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.NewShortNote> {
|
||||
ShortNotePostScreen(
|
||||
message = it.message,
|
||||
attachment = it.attachment?.ifBlank { null }?.toUri(),
|
||||
baseReplyTo = it.baseReplyTo?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
quote = it.quote?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
fork = it.fork?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
version = it.version?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
draft = it.draft?.let { hex -> accountViewModel.getNoteIfExists(hex) },
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
composableFromBottomArgs<Route.VoiceReply> {
|
||||
VoiceReplyScreen(
|
||||
replyToNoteId = it.replyToNoteId,
|
||||
recordingFilePath = it.recordingFilePath,
|
||||
mimeType = it.mimeType,
|
||||
duration = it.duration,
|
||||
amplitudesJson = it.amplitudes,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
composableFromBottomArgs<Route.VoiceReply> {
|
||||
VoiceReplyScreen(
|
||||
replyToNoteId = it.replyToNoteId,
|
||||
recordingFilePath = it.recordingFilePath,
|
||||
mimeType = it.mimeType,
|
||||
duration = it.duration,
|
||||
amplitudesJson = it.amplitudes,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,6 +510,18 @@ private fun NavigateIfIntentRequested(
|
||||
}
|
||||
}
|
||||
|
||||
private fun NavBackStackEntry.toBaseRoute(): Route? {
|
||||
val dest = destination
|
||||
return when {
|
||||
dest.hasRoute<Route.Home>() -> Route.Home
|
||||
dest.hasRoute<Route.Message>() -> Route.Message
|
||||
dest.hasRoute<Route.Video>() -> Route.Video
|
||||
dest.hasRoute<Route.Discover>() -> Route.Discover
|
||||
dest.hasRoute<Route.Notification>() -> Route.Notification
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun URI.findParameterValue(parameterName: String): String? =
|
||||
rawQuery
|
||||
?.split('&')
|
||||
|
||||
@@ -41,6 +41,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.vitorpamplona.amethyst.ui.layouts.isCompactWindow
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.painterRes
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -55,6 +56,8 @@ fun AppBottomBar(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (Route) -> Unit,
|
||||
) {
|
||||
if (!isCompactWindow()) return
|
||||
|
||||
val isKeyboardState by keyboardAsState()
|
||||
if (isKeyboardState == KeyboardState.Closed) {
|
||||
RenderBottomMenu(selectedRoute, accountViewModel, nav)
|
||||
|
||||
@@ -34,6 +34,7 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserPicture
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.layouts.isCompactWindow
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.note.SearchIcon
|
||||
@@ -61,7 +62,9 @@ fun UserDrawerSearchTopBar(
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
LoggedInUserPictureDrawer(accountViewModel, nav::openDrawer)
|
||||
if (isCompactWindow()) {
|
||||
LoggedInUserPictureDrawer(accountViewModel, nav::openDrawer)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { nav.nav(Route.Search) }) {
|
||||
|
||||
@@ -53,6 +53,7 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.PostingTopBar
|
||||
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
|
||||
@@ -177,11 +178,13 @@ fun GenericCommentPostScreen(
|
||||
.consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
) {
|
||||
GenericCommentPostBody(
|
||||
postViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
GenericCommentPostBody(
|
||||
postViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,23 +20,20 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.SheetValue
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import com.vitorpamplona.amethyst.ui.layouts.isCompactWindow
|
||||
import com.vitorpamplona.amethyst.ui.navigation.drawer.AccountSwitchBottomSheet
|
||||
import com.vitorpamplona.amethyst.ui.navigation.drawer.DrawerContent
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
@@ -71,25 +68,19 @@ fun AccountSwitcherAndLeftDrawerLayout(
|
||||
}
|
||||
}
|
||||
|
||||
val orientation = LocalConfiguration.current.orientation
|
||||
val currentDrawerState = nav.drawerState.currentValue
|
||||
LaunchedEffect(key1 = orientation) {
|
||||
if (
|
||||
orientation == Configuration.ORIENTATION_LANDSCAPE && currentDrawerState == DrawerValue.Closed
|
||||
) {
|
||||
nav.drawerState.close()
|
||||
}
|
||||
if (isCompactWindow()) {
|
||||
ModalNavigationDrawer(
|
||||
drawerState = nav.drawerState,
|
||||
drawerContent = {
|
||||
DrawerContent(nav, openSheetFunction, accountViewModel)
|
||||
BackHandler(enabled = nav.drawerState.isOpen, nav::closeDrawer)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
} else {
|
||||
content()
|
||||
}
|
||||
|
||||
ModalNavigationDrawer(
|
||||
drawerState = nav.drawerState,
|
||||
drawerContent = {
|
||||
DrawerContent(nav, openSheetFunction, accountViewModel)
|
||||
BackHandler(enabled = nav.drawerState.isOpen, nav::closeDrawer)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
|
||||
// Sheet content
|
||||
if (openAccountSwitcherBottomSheet) {
|
||||
ModalBottomSheet(
|
||||
|
||||
@@ -57,6 +57,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar
|
||||
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||
@@ -146,69 +147,71 @@ fun BookmarkGroupScreenView(
|
||||
}
|
||||
},
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
top = padding.calculateTopPadding(),
|
||||
bottom = padding.calculateBottomPadding(),
|
||||
).consumeWindowInsets(padding)
|
||||
.imePadding(),
|
||||
) {
|
||||
when (bookmarkType) {
|
||||
BookmarkType.PostBookmark -> {
|
||||
RenderPostList(
|
||||
bookmarkGroupViewModel,
|
||||
pagerState,
|
||||
accountViewModel,
|
||||
movePostBookmark = { postId, isPrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
bookmarkGroupViewModel.movePostBookmark(
|
||||
groupIdentifier = bookmarkGroupViewModel.bookmarkGroupIdentifier,
|
||||
postId = postId,
|
||||
isCurrentlyPrivate = isPrivate,
|
||||
)
|
||||
}
|
||||
},
|
||||
deletePostBookmark = { postId, isPrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
bookmarkGroupViewModel.removePostBookmark(
|
||||
bookmarkGroupViewModel.bookmarkGroupIdentifier,
|
||||
postId,
|
||||
isPrivate,
|
||||
)
|
||||
}
|
||||
},
|
||||
nav,
|
||||
)
|
||||
}
|
||||
MaxWidthContainer {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
top = padding.calculateTopPadding(),
|
||||
bottom = padding.calculateBottomPadding(),
|
||||
).consumeWindowInsets(padding)
|
||||
.imePadding(),
|
||||
) {
|
||||
when (bookmarkType) {
|
||||
BookmarkType.PostBookmark -> {
|
||||
RenderPostList(
|
||||
bookmarkGroupViewModel,
|
||||
pagerState,
|
||||
accountViewModel,
|
||||
movePostBookmark = { postId, isPrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
bookmarkGroupViewModel.movePostBookmark(
|
||||
groupIdentifier = bookmarkGroupViewModel.bookmarkGroupIdentifier,
|
||||
postId = postId,
|
||||
isCurrentlyPrivate = isPrivate,
|
||||
)
|
||||
}
|
||||
},
|
||||
deletePostBookmark = { postId, isPrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
bookmarkGroupViewModel.removePostBookmark(
|
||||
bookmarkGroupViewModel.bookmarkGroupIdentifier,
|
||||
postId,
|
||||
isPrivate,
|
||||
)
|
||||
}
|
||||
},
|
||||
nav,
|
||||
)
|
||||
}
|
||||
|
||||
BookmarkType.ArticleBookmark -> {
|
||||
RenderArticleList(
|
||||
bookmarkGroupViewModel,
|
||||
pagerState,
|
||||
accountViewModel,
|
||||
moveArticleBookmark = { articleAddress, isPrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
bookmarkGroupViewModel.moveArticleBookmark(
|
||||
groupIdentifier = bookmarkGroupViewModel.bookmarkGroupIdentifier,
|
||||
articleAddress = articleAddress,
|
||||
isCurrentlyPrivate = isPrivate,
|
||||
)
|
||||
}
|
||||
},
|
||||
deleteArticleBookmark = { articleAddress, isPrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
bookmarkGroupViewModel.removeArticleBookmark(
|
||||
bookmarkGroupViewModel.bookmarkGroupIdentifier,
|
||||
articleAddress,
|
||||
isPrivate,
|
||||
)
|
||||
}
|
||||
},
|
||||
nav,
|
||||
)
|
||||
BookmarkType.ArticleBookmark -> {
|
||||
RenderArticleList(
|
||||
bookmarkGroupViewModel,
|
||||
pagerState,
|
||||
accountViewModel,
|
||||
moveArticleBookmark = { articleAddress, isPrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
bookmarkGroupViewModel.moveArticleBookmark(
|
||||
groupIdentifier = bookmarkGroupViewModel.bookmarkGroupIdentifier,
|
||||
articleAddress = articleAddress,
|
||||
isCurrentlyPrivate = isPrivate,
|
||||
)
|
||||
}
|
||||
},
|
||||
deleteArticleBookmark = { articleAddress, isPrivate ->
|
||||
accountViewModel.launchSigner {
|
||||
bookmarkGroupViewModel.removeArticleBookmark(
|
||||
bookmarkGroupViewModel.bookmarkGroupIdentifier,
|
||||
articleAddress,
|
||||
isPrivate,
|
||||
)
|
||||
}
|
||||
},
|
||||
nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.BookmarkListState
|
||||
import com.vitorpamplona.amethyst.model.nip51Lists.labeledBookmarkLists.LabeledBookmarkList
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
@@ -106,23 +107,25 @@ fun ListOfBookmarkGroupsFeed(
|
||||
BookmarkGroupFab(onAddGroup = addBookmarkGroup)
|
||||
},
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
Modifier
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding(),
|
||||
).fillMaxHeight(),
|
||||
) {
|
||||
ListOfBookmarkGroupsFeedView(
|
||||
defaultBookmarks = defaultBookmarks,
|
||||
groupListFeedSource = listSource,
|
||||
openDefaultBookmarks = openDefaultBookmarks,
|
||||
onOpenItem = openBookmarkGroup,
|
||||
onRenameItem = renameBookmarkGroup,
|
||||
onItemDescriptionChange = changeBookmarkGroupDescription,
|
||||
onItemClone = cloneBookmarkGroup,
|
||||
onDeleteItem = deleteBookmarkGroup,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding(),
|
||||
).fillMaxHeight(),
|
||||
) {
|
||||
ListOfBookmarkGroupsFeedView(
|
||||
defaultBookmarks = defaultBookmarks,
|
||||
groupListFeedSource = listSource,
|
||||
openDefaultBookmarks = openDefaultBookmarks,
|
||||
onOpenItem = openBookmarkGroup,
|
||||
onRenameItem = renameBookmarkGroup,
|
||||
onItemDescriptionChange = changeBookmarkGroupDescription,
|
||||
onItemClone = cloneBookmarkGroup,
|
||||
onDeleteItem = deleteBookmarkGroup,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.header.RenderRoomTopBar
|
||||
@@ -50,16 +52,18 @@ fun ChatroomScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(Modifier.padding(it).consumeWindowInsets(it).statusBarsPadding()) {
|
||||
ChatroomView(
|
||||
room = roomId,
|
||||
draftMessage = draftMessage,
|
||||
replyToNote = replyToNote,
|
||||
editFromDraft = editFromDraft,
|
||||
expiresDays = expiresDays,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
MaxWidthContainer(maxWidth = 720.dp) {
|
||||
Column(Modifier.padding(it).consumeWindowInsets(it).statusBarsPadding()) {
|
||||
ChatroomView(
|
||||
room = roomId,
|
||||
draftMessage = draftMessage,
|
||||
replyToNote = replyToNote,
|
||||
editFromDraft = editFromDraft,
|
||||
expiresDays = expiresDays,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ephemChat.header.EphemeralChatTopBar
|
||||
@@ -48,8 +50,10 @@ fun EphemeralChatScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(Modifier.padding(it)) {
|
||||
EphemeralChatChannelView(channelId, draft, replyTo, accountViewModel, nav)
|
||||
MaxWidthContainer(maxWidth = 720.dp) {
|
||||
Column(Modifier.padding(it)) {
|
||||
EphemeralChatChannelView(channelId, draft, replyTo, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,10 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadPublicChatChannel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -51,8 +53,10 @@ fun PublicChatChannelScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(Modifier.padding(it)) {
|
||||
PublicChatChannelView(channelId, draft, replyTo, accountViewModel, nav)
|
||||
MaxWidthContainer(maxWidth = 720.dp) {
|
||||
Column(Modifier.padding(it)) {
|
||||
PublicChatChannelView(channelId, draft, replyTo, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.service.OnlineChecker
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.LoadLiveActivityChannel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -58,8 +60,10 @@ fun LiveActivityChannelScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(Modifier.padding(it)) {
|
||||
LiveActivityChannelView(channelId, draft, replyTo, accountViewModel, nav)
|
||||
MaxWidthContainer(maxWidth = 720.dp) {
|
||||
Column(Modifier.padding(it)) {
|
||||
LiveActivityChannelView(channelId, draft, replyTo, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TitleIconModifier
|
||||
@@ -191,27 +192,29 @@ fun CommunityScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
HorizontalPager(
|
||||
contentPadding = it,
|
||||
state = pagerState,
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
MaxWidthContainer {
|
||||
HorizontalPager(
|
||||
contentPadding = it,
|
||||
state = pagerState,
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
1 -> {
|
||||
RefresheableFeedView(
|
||||
modFeedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
1 -> {
|
||||
RefresheableFeedView(
|
||||
modFeedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
@@ -258,31 +259,33 @@ private fun DiscoverPages(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
HorizontalPager(state = pagerState, contentPadding = it) { page ->
|
||||
if (page >= 0 && page < feedTabs.size) {
|
||||
val tab = feedTabs[page]
|
||||
RefresheableBox(tab.feedState, true) {
|
||||
if (tab.useGridLayout) {
|
||||
SaveableGridFeedContentState(tab.feedState, scrollStateKey = tab.scrollStateKey) { listState ->
|
||||
RenderDiscoverFeed(
|
||||
feedContentState = tab.feedState,
|
||||
routeForLastRead = tab.routeForLastRead,
|
||||
forceEventKind = tab.forceEventKind,
|
||||
listState = listState,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
SaveableFeedContentState(tab.feedState, scrollStateKey = tab.scrollStateKey) { listState ->
|
||||
RenderDiscoverFeed(
|
||||
feedContentState = tab.feedState,
|
||||
routeForLastRead = tab.routeForLastRead,
|
||||
forceEventKind = tab.forceEventKind,
|
||||
listState = listState,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
HorizontalPager(state = pagerState, contentPadding = it) { page ->
|
||||
if (page >= 0 && page < feedTabs.size) {
|
||||
val tab = feedTabs[page]
|
||||
RefresheableBox(tab.feedState, true) {
|
||||
if (tab.useGridLayout) {
|
||||
SaveableGridFeedContentState(tab.feedState, scrollStateKey = tab.scrollStateKey) { listState ->
|
||||
RenderDiscoverFeed(
|
||||
feedContentState = tab.feedState,
|
||||
routeForLastRead = tab.routeForLastRead,
|
||||
forceEventKind = tab.forceEventKind,
|
||||
listState = listState,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
SaveableFeedContentState(tab.feedState, scrollStateKey = tab.scrollStateKey) { listState ->
|
||||
RenderDiscoverFeed(
|
||||
feedContentState = tab.feedState,
|
||||
routeForLastRead = tab.routeForLastRead,
|
||||
forceEventKind = tab.forceEventKind,
|
||||
listState = listState,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakePictureButton
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.TakeVideoButton
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.PostingTopBar
|
||||
@@ -173,11 +174,13 @@ fun NewProductScreen(
|
||||
.consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
) {
|
||||
NewProductBody(
|
||||
postViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
NewProductBody(
|
||||
postViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.vitorpamplona.amethyst.ui.components.ZapButtonConfig
|
||||
import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
|
||||
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent
|
||||
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
|
||||
@@ -97,19 +98,21 @@ fun DvmContentDiscoveryScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) { paddingValues ->
|
||||
Column(Modifier.padding(paddingValues)) {
|
||||
LoadNote(baseNoteHex = appDefinitionEventId, accountViewModel = accountViewModel) { note ->
|
||||
note?.let { baseNote ->
|
||||
WatchNoteEvent(
|
||||
baseNote,
|
||||
onNoteEventFound = {
|
||||
DvmContentDiscoveryScreen(baseNote, accountViewModel, nav)
|
||||
},
|
||||
onBlank = {
|
||||
FeedEmptyWithStatus(baseNote, stringRes(R.string.dvm_looking_for_app), accountViewModel, nav)
|
||||
},
|
||||
accountViewModel,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
Column(Modifier.padding(paddingValues)) {
|
||||
LoadNote(baseNoteHex = appDefinitionEventId, accountViewModel = accountViewModel) { note ->
|
||||
note?.let { baseNote ->
|
||||
WatchNoteEvent(
|
||||
baseNote,
|
||||
onNoteEventFound = {
|
||||
DvmContentDiscoveryScreen(baseNote, accountViewModel, nav)
|
||||
},
|
||||
onBlank = {
|
||||
FeedEmptyWithStatus(baseNote, stringRes(R.string.dvm_looking_for_app), accountViewModel, nav)
|
||||
},
|
||||
accountViewModel,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNo
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.ShorterTopAppBar
|
||||
@@ -171,35 +172,37 @@ fun FollowPackFeedScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
HorizontalPager(
|
||||
contentPadding = it,
|
||||
state = pagerState,
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> {
|
||||
RefresheableFeedView(
|
||||
newThreadFeedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
MaxWidthContainer {
|
||||
HorizontalPager(
|
||||
contentPadding = it,
|
||||
state = pagerState,
|
||||
) { page ->
|
||||
when (page) {
|
||||
0 -> {
|
||||
RefresheableFeedView(
|
||||
newThreadFeedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
1 -> {
|
||||
RefresheableFeedView(
|
||||
conversationsFeedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
1 -> {
|
||||
RefresheableFeedView(
|
||||
conversationsFeedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
|
||||
2 -> {
|
||||
UserFeedView(
|
||||
membersFeedViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
2 -> {
|
||||
UserFeedView(
|
||||
membersFeedViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowingGeohash
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBackButton
|
||||
@@ -102,13 +103,15 @@ fun GeoHashScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(Modifier.padding(it)) {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
Column(Modifier.padding(it)) {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowingHashtag
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBackButton
|
||||
@@ -107,13 +108,15 @@ fun HashtagScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(Modifier.padding(it)) {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
Column(Modifier.padding(it)) {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
@@ -208,19 +209,21 @@ private fun HomePages(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
HorizontalPager(
|
||||
contentPadding = it,
|
||||
state = pagerState,
|
||||
userScrollEnabled = false,
|
||||
) { page ->
|
||||
HomeFeeds(
|
||||
feedState = tabs[page].feedState,
|
||||
routeForLastRead = tabs[page].routeForLastRead,
|
||||
scrollStateKey = tabs[page].scrollStateKey,
|
||||
liveSection = tabs[page].liveSection,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
HorizontalPager(
|
||||
contentPadding = it,
|
||||
state = pagerState,
|
||||
userScrollEnabled = false,
|
||||
) { page ->
|
||||
HomeFeeds(
|
||||
feedState = tabs[page].feedState,
|
||||
routeForLastRead = tabs[page].routeForLastRead,
|
||||
scrollStateKey = tabs[page].scrollStateKey,
|
||||
liveSection = tabs[page].liveSection,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ import com.vitorpamplona.amethyst.ui.actions.uploads.UploadProgressIndicator
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceAnonymizationSection
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceMessagePreview
|
||||
import com.vitorpamplona.amethyst.ui.components.getActivity
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.PostingTopBar
|
||||
import com.vitorpamplona.amethyst.ui.note.BaseUserPicture
|
||||
@@ -216,7 +217,9 @@ private fun NewPostScreenInner(
|
||||
.consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
) {
|
||||
NewPostScreenBody(postViewModel, accountViewModel, nav)
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
NewPostScreenBody(postViewModel, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.vitorpamplona.amethyst.ui.actions.mediaServers.FileServerSelectionRow
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.UploadProgressIndicator
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceAnonymizationSection
|
||||
import com.vitorpamplona.amethyst.ui.actions.uploads.VoiceMessagePreview
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.Nav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.PostingTopBar
|
||||
import com.vitorpamplona.amethyst.ui.note.NoteCompose
|
||||
@@ -98,7 +99,9 @@ fun VoiceReplyScreen(
|
||||
.padding(pad)
|
||||
.consumeWindowInsets(pad),
|
||||
) {
|
||||
VoiceReplyScreenBody(viewModel, accountViewModel, nav)
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
VoiceReplyScreenBody(viewModel, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import com.vitorpamplona.amethyst.model.AddressableNote
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.ui.components.ClickableBox
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
@@ -136,20 +137,22 @@ fun PeopleListScreen(
|
||||
}
|
||||
},
|
||||
) { padding ->
|
||||
ListViewAndEditColumn(
|
||||
viewModel = viewModel,
|
||||
pagerState = pagerState,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
top = padding.calculateTopPadding(),
|
||||
bottom = padding.calculateBottomPadding(),
|
||||
).consumeWindowInsets(padding)
|
||||
.imePadding(),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
ListViewAndEditColumn(
|
||||
viewModel = viewModel,
|
||||
pagerState = pagerState,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
top = padding.calculateTopPadding(),
|
||||
bottom = padding.calculateBottomPadding(),
|
||||
).consumeWindowInsets(padding)
|
||||
.imePadding(),
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -66,14 +67,16 @@ fun ListOfPeopleListsScreen(
|
||||
TopBarWithBackButton(stringRes(R.string.my_lists), nav::popBack)
|
||||
},
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
Modifier
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding(),
|
||||
).fillMaxHeight(),
|
||||
) {
|
||||
AllPeopleListFeedView(list, pack, nav)
|
||||
MaxWidthContainer {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding(),
|
||||
).fillMaxHeight(),
|
||||
) {
|
||||
AllPeopleListFeedView(list, pack, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.vitorpamplona.amethyst.model.UiSettingsFlow
|
||||
import com.vitorpamplona.amethyst.ui.components.SelectNotificationProvider
|
||||
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
@@ -84,17 +85,19 @@ fun NotificationScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(it).consumeWindowInsets(it),
|
||||
) {
|
||||
ObserveInboxRelayListAndDisplayIfNotFound(accountViewModel, nav)
|
||||
RefreshableCardView(
|
||||
feedContent = notifFeedContentState,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
routeForLastRead = "Notification",
|
||||
scrollStateKey = ScrollStateKeys.NOTIFICATION_SCREEN,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
Column(
|
||||
modifier = Modifier.padding(it).consumeWindowInsets(it),
|
||||
) {
|
||||
ObserveInboxRelayListAndDisplayIfNotFound(accountViewModel, nav)
|
||||
RefreshableCardView(
|
||||
feedContent = notifFeedContentState,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
routeForLastRead = "Notification",
|
||||
scrollStateKey = ScrollStateKeys.NOTIFICATION_SCREEN,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,14 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile
|
||||
import androidx.compose.foundation.gestures.scrollBy
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
@@ -40,6 +42,7 @@ import androidx.compose.material3.ScrollableTabRow
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.VerticalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -56,12 +59,15 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.window.core.layout.WindowWidthSizeClass
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.LocalCache
|
||||
import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.observeAccountIsHiddenUser
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.LocalWindowSizeClass
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.bookmarks.BookmarkTabHeader
|
||||
@@ -277,11 +283,11 @@ fun ProfileScreen(
|
||||
|
||||
UserProfileFilterAssemblerSubscription(baseUser, accountViewModel.dataSources().profile)
|
||||
|
||||
RenderSurface { tabRowModifier: Modifier, pagerModifier: Modifier ->
|
||||
RenderScreen(
|
||||
val isExpanded = LocalWindowSizeClass.current == WindowWidthSizeClass.EXPANDED
|
||||
|
||||
if (isExpanded) {
|
||||
RenderExpandedScreen(
|
||||
baseUser,
|
||||
tabRowModifier,
|
||||
pagerModifier,
|
||||
threadsViewModel,
|
||||
repliesViewModel,
|
||||
mutualViewModel,
|
||||
@@ -296,6 +302,27 @@ fun ProfileScreen(
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
} else {
|
||||
RenderSurface { tabRowModifier: Modifier, pagerModifier: Modifier ->
|
||||
RenderScreen(
|
||||
baseUser,
|
||||
tabRowModifier,
|
||||
pagerModifier,
|
||||
threadsViewModel,
|
||||
repliesViewModel,
|
||||
mutualViewModel,
|
||||
appRecommendations,
|
||||
externalIdentities,
|
||||
followsFeedViewModel,
|
||||
followersFeedViewModel,
|
||||
zapFeedViewModel,
|
||||
bookmarksFeedViewModel,
|
||||
galleryFeedViewModel,
|
||||
reportsFeedViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,6 +398,106 @@ private fun RenderSurface(content: @Composable (tabRowModifier: Modifier, pagerM
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderExpandedScreen(
|
||||
baseUser: User,
|
||||
threadsViewModel: UserProfileNewThreadsFeedViewModel,
|
||||
repliesViewModel: UserProfileConversationsFeedViewModel,
|
||||
mutualViewModel: UserProfileMutualFeedViewModel,
|
||||
appRecommendations: UserAppRecommendationsFeedViewModel,
|
||||
externalIdentities: UserExternalIdentitiesViewModel,
|
||||
followsFeedViewModel: UserProfileFollowsUserFeedViewModel,
|
||||
followersFeedViewModel: UserProfileFollowersUserFeedViewModel,
|
||||
zapFeedViewModel: UserProfileZapsViewModel,
|
||||
bookmarksFeedViewModel: UserProfileBookmarksFeedViewModel,
|
||||
galleryFeedViewModel: UserProfileGalleryFeedViewModel,
|
||||
reportsFeedViewModel: UserProfileReportFeedViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val pagerState = rememberPagerState { 11 }
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxSize()) {
|
||||
// Left side pane: profile header in a scrollable column
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.width(320.dp)
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
ProfileHeader(baseUser, appRecommendations, externalIdentities, nav, accountViewModel)
|
||||
}
|
||||
|
||||
VerticalDivider(thickness = DividerThickness)
|
||||
|
||||
// Right side: tabs + pager filling remaining space
|
||||
var columnSize by remember { mutableStateOf(IntSize.Zero) }
|
||||
var tabsSize by remember { mutableStateOf(IntSize.Zero) }
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight()
|
||||
.onSizeChanged { columnSize = it },
|
||||
) {
|
||||
val tabRowModifier = remember { Modifier.onSizeChanged { tabsSize = it } }
|
||||
val pagerModifier =
|
||||
with(LocalDensity.current) { Modifier.height((columnSize.height - tabsSize.height).toDp()) }
|
||||
|
||||
ScrollableTabRow(
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||
selectedTabIndex = pagerState.currentPage,
|
||||
edgePadding = Size8dp,
|
||||
modifier = tabRowModifier,
|
||||
divider = { HorizontalDivider(thickness = DividerThickness) },
|
||||
) {
|
||||
CreateAndRenderTabs(
|
||||
baseUser,
|
||||
pagerState,
|
||||
threadsViewModel,
|
||||
repliesViewModel,
|
||||
mutualViewModel,
|
||||
followsFeedViewModel,
|
||||
followersFeedViewModel,
|
||||
zapFeedViewModel,
|
||||
bookmarksFeedViewModel,
|
||||
galleryFeedViewModel,
|
||||
reportsFeedViewModel,
|
||||
accountViewModel,
|
||||
)
|
||||
}
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
modifier = pagerModifier,
|
||||
) { page ->
|
||||
CreateAndRenderPages(
|
||||
page,
|
||||
baseUser,
|
||||
threadsViewModel,
|
||||
repliesViewModel,
|
||||
mutualViewModel,
|
||||
followsFeedViewModel,
|
||||
followersFeedViewModel,
|
||||
zapFeedViewModel,
|
||||
bookmarksFeedViewModel,
|
||||
galleryFeedViewModel,
|
||||
reportsFeedViewModel,
|
||||
accountViewModel,
|
||||
nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenderScreen(
|
||||
baseUser: User,
|
||||
|
||||
@@ -55,6 +55,7 @@ import com.vitorpamplona.amethyst.model.User
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserInfo
|
||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
@@ -147,20 +148,22 @@ fun ShowQRScreen(
|
||||
)
|
||||
},
|
||||
) { pad ->
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
start = 20.dp,
|
||||
end = 20.dp,
|
||||
top = pad.calculateTopPadding(),
|
||||
bottom = pad.calculateBottomPadding(),
|
||||
).consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
ShowQRBody(user, accountViewModel, nav)
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
start = 20.dp,
|
||||
end = 20.dp,
|
||||
top = pad.calculateTopPadding(),
|
||||
bottom = pad.calculateBottomPadding(),
|
||||
).consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
ShowQRBody(user, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.observeUserIsFollowingRelay
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBackButton
|
||||
@@ -105,13 +106,15 @@ fun RelayFeedScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(Modifier.padding(it)) {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
Column(Modifier.padding(it)) {
|
||||
RefresheableFeedView(
|
||||
feedViewModel,
|
||||
null,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.DefaultDMRelayList
|
||||
import com.vitorpamplona.amethyst.model.DefaultIndexerRelayList
|
||||
import com.vitorpamplona.amethyst.model.DefaultSearchRelayList
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -210,142 +211,144 @@ fun MappedAllRelayListView(
|
||||
)
|
||||
},
|
||||
) { pad ->
|
||||
LazyColumn(
|
||||
contentPadding = FeedPadding,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
start = 10.dp,
|
||||
end = 10.dp,
|
||||
top = pad.calculateTopPadding(),
|
||||
bottom = pad.calculateBottomPadding(),
|
||||
).consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
) {
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.public_home_section,
|
||||
R.string.public_home_section_explainer,
|
||||
SettingsCategoryFirstModifier,
|
||||
)
|
||||
}
|
||||
renderNip65HomeItems(homeFeedState, nip65ViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.public_notif_section,
|
||||
R.string.public_notif_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderNip65NotifItems(notifFeedState, nip65ViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
R.string.private_inbox_section,
|
||||
R.string.private_inbox_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
action = {
|
||||
ResetDMRelays(dmViewModel)
|
||||
},
|
||||
)
|
||||
}
|
||||
renderDMItems(dmFeedState, dmViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.private_outbox_section,
|
||||
R.string.private_outbox_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderPrivateOutboxItems(privateOutboxFeedState, privateOutboxViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.proxy_section,
|
||||
R.string.proxy_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderProxyItems(proxyRelays, proxyViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.broadcast_section,
|
||||
R.string.broadcast_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderBroadcastItems(broadcastRelays, broadcastViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
R.string.indexer_section,
|
||||
R.string.indexer_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
) {
|
||||
ResetIndexerRelays(indexerViewModel)
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
LazyColumn(
|
||||
contentPadding = FeedPadding,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
start = 10.dp,
|
||||
end = 10.dp,
|
||||
top = pad.calculateTopPadding(),
|
||||
bottom = pad.calculateBottomPadding(),
|
||||
).consumeWindowInsets(pad)
|
||||
.imePadding(),
|
||||
) {
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.public_home_section,
|
||||
R.string.public_home_section_explainer,
|
||||
SettingsCategoryFirstModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
renderIndexerItems(indexerRelays, indexerViewModel, accountViewModel, newNav)
|
||||
renderNip65HomeItems(homeFeedState, nip65ViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
R.string.search_section,
|
||||
R.string.search_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
) {
|
||||
ResetSearchRelays(searchViewModel)
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.public_notif_section,
|
||||
R.string.public_notif_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
renderSearchItems(searchFeedState, searchViewModel, accountViewModel, newNav)
|
||||
renderNip65NotifItems(notifFeedState, nip65ViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.local_section,
|
||||
R.string.local_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderLocalItems(localFeedState, localViewModel, accountViewModel, newNav)
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
R.string.private_inbox_section,
|
||||
R.string.private_inbox_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
action = {
|
||||
ResetDMRelays(dmViewModel)
|
||||
},
|
||||
)
|
||||
}
|
||||
renderDMItems(dmFeedState, dmViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.trusted_section,
|
||||
R.string.trusted_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderTrustedItems(trustedFeedState, trustedViewModel, accountViewModel, newNav)
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.private_outbox_section,
|
||||
R.string.private_outbox_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderPrivateOutboxItems(privateOutboxFeedState, privateOutboxViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.favorite_section,
|
||||
R.string.favorite_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderRelayFeedsItems(relayFeedsFeedState, relayFeedsViewModel, accountViewModel, newNav)
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.proxy_section,
|
||||
R.string.proxy_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderProxyItems(proxyRelays, proxyViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.blocked_section,
|
||||
R.string.blocked_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderBlockedItems(blockedFeedState, blockedViewModel, accountViewModel, newNav)
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.broadcast_section,
|
||||
R.string.broadcast_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderBroadcastItems(broadcastRelays, broadcastViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.connected_section,
|
||||
R.string.connected_section_description,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
R.string.indexer_section,
|
||||
R.string.indexer_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
) {
|
||||
ResetIndexerRelays(indexerViewModel)
|
||||
}
|
||||
}
|
||||
renderIndexerItems(indexerRelays, indexerViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategoryWithButton(
|
||||
R.string.search_section,
|
||||
R.string.search_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
) {
|
||||
ResetSearchRelays(searchViewModel)
|
||||
}
|
||||
}
|
||||
renderSearchItems(searchFeedState, searchViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.local_section,
|
||||
R.string.local_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderLocalItems(localFeedState, localViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.trusted_section,
|
||||
R.string.trusted_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderTrustedItems(trustedFeedState, trustedViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.favorite_section,
|
||||
R.string.favorite_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderRelayFeedsItems(relayFeedsFeedState, relayFeedsViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.blocked_section,
|
||||
R.string.blocked_section_explainer,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderBlockedItems(blockedFeedState, blockedViewModel, accountViewModel, newNav)
|
||||
|
||||
item {
|
||||
SettingsCategory(
|
||||
R.string.connected_section,
|
||||
R.string.connected_section_description,
|
||||
SettingsCategorySpacingModifier,
|
||||
)
|
||||
}
|
||||
renderConnectedItems(connectedRelays, connectedViewModel, accountViewModel, newNav)
|
||||
}
|
||||
renderConnectedItems(connectedRelays, connectedViewModel, accountViewModel, newNav)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.model.nip11RelayInfo.loadRelayInfo
|
||||
import com.vitorpamplona.amethyst.service.relayClient.searchCommand.TextSearchDataSourceSubscription
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
@@ -124,11 +125,13 @@ fun SearchScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(it).consumeWindowInsets(it),
|
||||
) {
|
||||
ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav)
|
||||
DisplaySearchResults(searchBarViewModel, nav, accountViewModel)
|
||||
MaxWidthContainer {
|
||||
Column(
|
||||
modifier = Modifier.padding(it).consumeWindowInsets(it),
|
||||
) {
|
||||
ObserveRelayListForSearchAndDisplayIfNotFound(accountViewModel, nav)
|
||||
DisplaySearchResults(searchBarViewModel, nav, accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) 2025 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.screen.loggedIn.settings
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.layout.AnimatedPane
|
||||
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
|
||||
import androidx.compose.material3.adaptive.navigation.NavigableListDetailPaneScaffold
|
||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.vitorpamplona.amethyst.Amethyst
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.layouts.isCompactWindow
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun AdaptiveSettingsScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
if (isCompactWindow()) {
|
||||
AllSettingsScreen(accountViewModel, nav)
|
||||
} else {
|
||||
AdaptiveSettingsListDetail(accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
private fun AdaptiveSettingsListDetail(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val navigator = rememberListDetailPaneScaffoldNavigator<Route>()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
BackHandler(navigator.canNavigateBack()) {
|
||||
scope.launch {
|
||||
navigator.navigateBack()
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBarWithBackButton(stringRes(id = R.string.settings), nav::popBack)
|
||||
},
|
||||
) { padding ->
|
||||
NavigableListDetailPaneScaffold(
|
||||
navigator = navigator,
|
||||
listPane = {
|
||||
AnimatedPane {
|
||||
AllSettingsContent(
|
||||
accountViewModel = accountViewModel,
|
||||
onSettingsNav = { route ->
|
||||
scope.launch {
|
||||
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, route)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
detailPane = {
|
||||
AnimatedPane {
|
||||
val selectedRoute = navigator.currentDestination?.contentKey
|
||||
if (selectedRoute != null) {
|
||||
SettingsDetailContent(
|
||||
route = selectedRoute,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
} else {
|
||||
SettingsDetailPlaceholder()
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(padding),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsDetailContent(
|
||||
route: Route,
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
when (route) {
|
||||
is Route.EditRelays -> {
|
||||
com.vitorpamplona.amethyst.ui.screen.loggedIn.relays
|
||||
.AllRelayListScreen(accountViewModel, nav)
|
||||
}
|
||||
|
||||
is Route.EditMediaServers -> {
|
||||
com.vitorpamplona.amethyst.ui.actions.mediaServers
|
||||
.AllMediaServersScreen(accountViewModel, nav)
|
||||
}
|
||||
|
||||
is Route.UpdateReactionType -> {
|
||||
com.vitorpamplona.amethyst.ui.note
|
||||
.UpdateReactionTypeScreen(accountViewModel, nav)
|
||||
}
|
||||
|
||||
is Route.UpdateZapAmount -> {
|
||||
UpdateZapAmountScreen(accountViewModel, nav, route.nip47)
|
||||
}
|
||||
|
||||
is Route.SecurityFilters -> {
|
||||
SecurityFiltersScreen(accountViewModel, nav)
|
||||
}
|
||||
|
||||
is Route.UserSettings -> {
|
||||
UserSettingsScreen(accountViewModel, nav)
|
||||
}
|
||||
|
||||
is Route.AccountBackup -> {
|
||||
com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup
|
||||
.AccountBackupScreen(accountViewModel, nav)
|
||||
}
|
||||
|
||||
is Route.PrivacyOptions -> {
|
||||
com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy
|
||||
.PrivacyOptionsScreen(Amethyst.instance.torPrefs.value, nav)
|
||||
}
|
||||
|
||||
is Route.NamecoinSettings -> {
|
||||
NamecoinSettingsScreen(Amethyst.instance.namecoinPrefs, nav)
|
||||
}
|
||||
|
||||
is Route.Settings -> {
|
||||
SettingsScreen(accountViewModel, nav)
|
||||
}
|
||||
|
||||
is Route.ReactionsSettings -> {
|
||||
ReactionsSettingsScreen(accountViewModel, nav)
|
||||
}
|
||||
|
||||
else -> {
|
||||
SettingsDetailPlaceholder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsDetailPlaceholder() {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringRes(R.string.settings),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -76,97 +76,110 @@ fun AllSettingsScreen(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: INav,
|
||||
) {
|
||||
val tint = MaterialTheme.colorScheme.onBackground
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBarWithBackButton(stringRes(id = R.string.settings), nav::popBack)
|
||||
},
|
||||
) { padding ->
|
||||
Column(Modifier.padding(padding)) {
|
||||
SettingsSectionHeader(R.string.account_settings)
|
||||
SettingsNavigationRow(
|
||||
title = R.string.relay_setup,
|
||||
iconPainter = R.drawable.relays,
|
||||
iconPainterRef = 4,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.EditRelays) },
|
||||
)
|
||||
AllSettingsContent(
|
||||
accountViewModel = accountViewModel,
|
||||
onSettingsNav = { route -> nav.nav(route) },
|
||||
modifier = Modifier.padding(padding),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AllSettingsContent(
|
||||
accountViewModel: AccountViewModel,
|
||||
onSettingsNav: (Route) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val tint = MaterialTheme.colorScheme.onBackground
|
||||
|
||||
Column(modifier) {
|
||||
SettingsSectionHeader(R.string.account_settings)
|
||||
SettingsNavigationRow(
|
||||
title = R.string.relay_setup,
|
||||
iconPainter = R.drawable.relays,
|
||||
iconPainterRef = 4,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.EditRelays) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.media_servers,
|
||||
icon = Icons.Outlined.CloudUpload,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.EditMediaServers) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.reactions,
|
||||
icon = Icons.Outlined.FavoriteBorder,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.UpdateReactionType) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.zaps,
|
||||
icon = Icons.Outlined.Bolt,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.UpdateZapAmount()) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.security_filters,
|
||||
icon = Icons.Outlined.Security,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.SecurityFilters) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.translations,
|
||||
icon = Icons.Outlined.Translate,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.UserSettings) },
|
||||
)
|
||||
accountViewModel.account.settings.keyPair.privKey?.let {
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.media_servers,
|
||||
icon = Icons.Outlined.CloudUpload,
|
||||
title = R.string.backup_keys,
|
||||
icon = Icons.Outlined.Key,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.EditMediaServers) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.reactions,
|
||||
icon = Icons.Outlined.FavoriteBorder,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.UpdateReactionType) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.zaps,
|
||||
icon = Icons.Outlined.Bolt,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.UpdateZapAmount()) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.security_filters,
|
||||
icon = Icons.Outlined.Security,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.SecurityFilters) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.translations,
|
||||
icon = Icons.Outlined.Translate,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.UserSettings) },
|
||||
)
|
||||
accountViewModel.account.settings.keyPair.privKey?.let {
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.backup_keys,
|
||||
icon = Icons.Outlined.Key,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.AccountBackup) },
|
||||
)
|
||||
}
|
||||
HorizontalDivider(thickness = 4.dp)
|
||||
SettingsSectionHeader(R.string.app_settings)
|
||||
SettingsNavigationRow(
|
||||
title = R.string.privacy_options,
|
||||
iconPainter = R.drawable.ic_tor,
|
||||
iconPainterRef = 1,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.PrivacyOptions) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.namecoin_settings,
|
||||
icon = Icons.Outlined.Security,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.NamecoinSettings) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.ui_preferences,
|
||||
icon = Icons.Outlined.Settings,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.Settings) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.reactions_settings,
|
||||
icon = Icons.Outlined.ThumbUp,
|
||||
tint = tint,
|
||||
onClick = { nav.nav(Route.ReactionsSettings) },
|
||||
onClick = { onSettingsNav(Route.AccountBackup) },
|
||||
)
|
||||
}
|
||||
HorizontalDivider(thickness = 4.dp)
|
||||
SettingsSectionHeader(R.string.app_settings)
|
||||
SettingsNavigationRow(
|
||||
title = R.string.privacy_options,
|
||||
iconPainter = R.drawable.ic_tor,
|
||||
iconPainterRef = 1,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.PrivacyOptions) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.namecoin_settings,
|
||||
icon = Icons.Outlined.Security,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.NamecoinSettings) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.ui_preferences,
|
||||
icon = Icons.Outlined.Settings,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.Settings) },
|
||||
)
|
||||
HorizontalDivider()
|
||||
SettingsNavigationRow(
|
||||
title = R.string.reactions_settings,
|
||||
icon = Icons.Outlined.ThumbUp,
|
||||
tint = tint,
|
||||
onClick = { onSettingsNav(Route.ReactionsSettings) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,10 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
|
||||
import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountContent
|
||||
@@ -75,11 +77,13 @@ fun NIP47SetupScreen(
|
||||
)
|
||||
},
|
||||
) {
|
||||
Column(Modifier.padding(it)) {
|
||||
UpdateZapAmountContent(postViewModel, onClose = {
|
||||
postViewModel.cancel()
|
||||
nav.popBack()
|
||||
}, nip47, accountViewModel)
|
||||
MaxWidthContainer(maxWidth = 560.dp) {
|
||||
Column(Modifier.padding(it)) {
|
||||
UpdateZapAmountContent(postViewModel, onClose = {
|
||||
postViewModel.cancel()
|
||||
nav.popBack()
|
||||
}, nip47, accountViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.EventFind
|
||||
import com.vitorpamplona.amethyst.ui.components.LoadNote
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarExtensibleWithBackButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
@@ -72,8 +73,10 @@ fun ThreadScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(Modifier.padding(it)) {
|
||||
ThreadFeedView(noteId, feedViewModel, accountViewModel, nav)
|
||||
MaxWidthContainer {
|
||||
Column(Modifier.padding(it)) {
|
||||
ThreadFeedView(noteId, feedViewModel, accountViewModel, nav)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ import com.vitorpamplona.amethyst.ui.feeds.WatchLifecycleAndUpdateModel
|
||||
import com.vitorpamplona.amethyst.ui.feeds.WatchScrollToTop
|
||||
import com.vitorpamplona.amethyst.ui.feeds.rememberForeverPagerState
|
||||
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
|
||||
import com.vitorpamplona.amethyst.ui.layouts.MaxWidthContainer
|
||||
import com.vitorpamplona.amethyst.ui.navigation.bottombars.AppBottomBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
@@ -143,15 +144,17 @@ fun VideoScreen(
|
||||
},
|
||||
accountViewModel = accountViewModel,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(it).consumeWindowInsets(it),
|
||||
) {
|
||||
RenderPage(
|
||||
videoFeedContentState = videoFeedContentState,
|
||||
pagerStateKey = ScrollStateKeys.VIDEO_SCREEN,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
MaxWidthContainer {
|
||||
Column(
|
||||
modifier = Modifier.padding(it).consumeWindowInsets(it),
|
||||
) {
|
||||
RenderPage(
|
||||
videoFeedContentState = videoFeedContentState,
|
||||
pagerStateKey = ScrollStateKeys.VIDEO_SCREEN,
|
||||
accountViewModel = accountViewModel,
|
||||
nav = nav,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ zelory = "3.0.1"
|
||||
zoomable = "2.11.1"
|
||||
zxing = "3.5.4"
|
||||
zxingAndroidEmbedded = "4.3.0"
|
||||
material3Adaptive = "1.1.0"
|
||||
windowCoreAndroid = "1.5.1"
|
||||
androidxCamera = "1.5.3"
|
||||
androidxCollection = "1.5.0"
|
||||
@@ -95,6 +96,10 @@ androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecyc
|
||||
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-material-icons = { group = "androidx.compose.material", name = "material-icons-extended" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
androidx-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive", version.ref = "material3Adaptive" }
|
||||
androidx-material3-adaptive-layout = { group = "androidx.compose.material3.adaptive", name = "adaptive-layout", version.ref = "material3Adaptive" }
|
||||
androidx-material3-adaptive-navigation = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation", version.ref = "material3Adaptive" }
|
||||
androidx-material3-adaptive-navigation-suite = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite" }
|
||||
androidx-material3-windowSize = { group = "androidx.compose.material3", name = "material3-window-size-class" }
|
||||
androidx-media3-datasource-okhttp = { group = "androidx.media3", name = "media3-datasource-okhttp", version.ref = "media3" }
|
||||
androidx-media3-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
|
||||
|
||||
Reference in New Issue
Block a user