Moves the QR Code screen from Dialog to full Route
This commit is contained in:
@@ -90,6 +90,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationS
|
|||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.publicMessages.NewPublicMessageScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.publicMessages.NewPublicMessageScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy.PrivacyOptionsScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy.PrivacyOptionsScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.ShowQRScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.redirect.LoadRedirectScreen
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.redirect.LoadRedirectScreen
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.AllRelayListScreen
|
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.relays.RelayInformationScreen
|
||||||
@@ -138,6 +139,8 @@ fun AppNavigation(
|
|||||||
composableFromBottomArgs<Route.PeopleListMetadataEdit> { PeopleListMetadataScreen(it.dTag, accountViewModel, nav) }
|
composableFromBottomArgs<Route.PeopleListMetadataEdit> { PeopleListMetadataScreen(it.dTag, accountViewModel, nav) }
|
||||||
composableFromBottomArgs<Route.FollowPackMetadataEdit> { FollowPackMetadataScreen(it.dTag, accountViewModel, nav) }
|
composableFromBottomArgs<Route.FollowPackMetadataEdit> { FollowPackMetadataScreen(it.dTag, accountViewModel, nav) }
|
||||||
|
|
||||||
|
composableFromBottomArgs<Route.QRDisplay> { ShowQRScreen(it.pubkey, accountViewModel, nav) }
|
||||||
|
|
||||||
composableFromBottomArgs<Route.ManualZapSplitPayment> { PayViaIntentScreen(it.paymentId, accountViewModel, nav) }
|
composableFromBottomArgs<Route.ManualZapSplitPayment> { PayViaIntentScreen(it.paymentId, accountViewModel, nav) }
|
||||||
|
|
||||||
composableFromBottomArgs<Route.EditProfile> { NewUserMetadataScreen(nav, accountViewModel) }
|
composableFromBottomArgs<Route.EditProfile> { NewUserMetadataScreen(nav, accountViewModel) }
|
||||||
|
|||||||
+1
-18
@@ -105,7 +105,6 @@ import com.vitorpamplona.amethyst.ui.note.LoadStatuses
|
|||||||
import com.vitorpamplona.amethyst.ui.painterRes
|
import com.vitorpamplona.amethyst.ui.painterRes
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.AccountBackupDialog
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.keyBackup.AccountBackupDialog
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.ShowQRDialog
|
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||||
@@ -713,9 +712,6 @@ fun BottomContent(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
// store the dialog open or close state
|
|
||||||
var dialogOpen by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Column(modifier = Modifier) {
|
Column(modifier = Modifier) {
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
modifier = Modifier.padding(top = 15.dp),
|
modifier = Modifier.padding(top = 15.dp),
|
||||||
@@ -759,7 +755,7 @@ fun BottomContent(
|
|||||||
Box(modifier = Modifier.weight(1F))
|
Box(modifier = Modifier.weight(1F))
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
dialogOpen = true
|
nav.nav(Route.QRDisplay(user.pubkeyHex))
|
||||||
nav.closeDrawer()
|
nav.closeDrawer()
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
@@ -772,17 +768,4 @@ fun BottomContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialogOpen) {
|
|
||||||
ShowQRDialog(
|
|
||||||
user,
|
|
||||||
accountViewModel,
|
|
||||||
onScan = {
|
|
||||||
dialogOpen = false
|
|
||||||
nav.closeDrawer()
|
|
||||||
nav.nav(it)
|
|
||||||
},
|
|
||||||
onClose = { dialogOpen = false },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ sealed class Route {
|
|||||||
val id: String,
|
val id: String,
|
||||||
) : Route()
|
) : Route()
|
||||||
|
|
||||||
|
@Serializable data class QRDisplay(
|
||||||
|
val pubkey: String,
|
||||||
|
) : Route()
|
||||||
|
|
||||||
@Serializable data class ContentDiscovery(
|
@Serializable data class ContentDiscovery(
|
||||||
val id: String,
|
val id: String,
|
||||||
) : Route()
|
) : Route()
|
||||||
@@ -287,6 +291,7 @@ fun getRouteWithArguments(navController: NavHostController): Route? {
|
|||||||
dest.hasRoute<Route.Geohash>() -> entry.toRoute<Route.Geohash>()
|
dest.hasRoute<Route.Geohash>() -> entry.toRoute<Route.Geohash>()
|
||||||
dest.hasRoute<Route.Community>() -> entry.toRoute<Route.Community>()
|
dest.hasRoute<Route.Community>() -> entry.toRoute<Route.Community>()
|
||||||
|
|
||||||
|
dest.hasRoute<Route.QRDisplay>() -> entry.toRoute<Route.QRDisplay>()
|
||||||
dest.hasRoute<Route.RelayInfo>() -> entry.toRoute<Route.RelayInfo>()
|
dest.hasRoute<Route.RelayInfo>() -> entry.toRoute<Route.RelayInfo>()
|
||||||
|
|
||||||
dest.hasRoute<Route.RoomByAuthor>() -> entry.toRoute<Route.RoomByAuthor>()
|
dest.hasRoute<Route.RoomByAuthor>() -> entry.toRoute<Route.RoomByAuthor>()
|
||||||
|
|||||||
+2
-16
@@ -53,13 +53,13 @@ import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
|||||||
import com.vitorpamplona.amethyst.ui.components.DisplayNip05ProfileStatus
|
import com.vitorpamplona.amethyst.ui.components.DisplayNip05ProfileStatus
|
||||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||||
import com.vitorpamplona.amethyst.ui.note.DrawPlayName
|
import com.vitorpamplona.amethyst.ui.note.DrawPlayName
|
||||||
import com.vitorpamplona.amethyst.ui.painterRes
|
import com.vitorpamplona.amethyst.ui.painterRes
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.DisplayAppRecommendations
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.DisplayAppRecommendations
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.apps.UserAppRecommendationsFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.badges.DisplayBadges
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header.badges.DisplayBadges
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.ShowQRDialog
|
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
|
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size25Modifier
|
import com.vitorpamplona.amethyst.ui.theme.Size25Modifier
|
||||||
@@ -127,23 +127,9 @@ fun DrawAdditionalInfo(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var dialogOpen by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
if (dialogOpen) {
|
|
||||||
ShowQRDialog(
|
|
||||||
user = user,
|
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
onScan = {
|
|
||||||
dialogOpen = false
|
|
||||||
nav.nav(it)
|
|
||||||
},
|
|
||||||
onClose = { dialogOpen = false },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Size25Modifier,
|
modifier = Size25Modifier,
|
||||||
onClick = { dialogOpen = true },
|
onClick = { nav.nav(Route.QRDisplay(user.pubkeyHex)) },
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterRes(R.drawable.ic_qrcode, 1),
|
painter = painterRes(R.drawable.ic_qrcode, 1),
|
||||||
|
|||||||
+2
@@ -43,6 +43,8 @@ fun NIP19QrCodeScanner(
|
|||||||
try {
|
try {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
onScan(uriToRoute(it, accountViewModel.account))
|
onScan(uriToRoute(it, accountViewModel.account))
|
||||||
|
} else {
|
||||||
|
onScan(null)
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
|
|||||||
-236
@@ -1,236 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.qrcode
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.imePadding
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.FilledTonalButton
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
|
||||||
import com.vitorpamplona.amethyst.R
|
|
||||||
import com.vitorpamplona.amethyst.model.User
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.DisplayNIP05
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
|
|
||||||
import com.vitorpamplona.amethyst.ui.components.nip05VerificationAsAState
|
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
|
||||||
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.largeProfilePictureModifier
|
|
||||||
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun ShowQRDialogPreview() {
|
|
||||||
val accountViewModel = mockAccountViewModel()
|
|
||||||
accountViewModel.userProfile().info =
|
|
||||||
UserMetadata().apply {
|
|
||||||
name = "My Name"
|
|
||||||
picture = "Picture"
|
|
||||||
nip05 = null
|
|
||||||
banner = "http://banner.com/test"
|
|
||||||
website = "http://mywebsite.com/test"
|
|
||||||
about = "This is the about me"
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowQRDialog(
|
|
||||||
user = accountViewModel.userProfile(),
|
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
onScan = {},
|
|
||||||
onClose = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun BackButton(onPress: () -> Unit) {
|
|
||||||
IconButton(
|
|
||||||
onClick = onPress,
|
|
||||||
) {
|
|
||||||
ArrowBackIcon(MaterialTheme.colorScheme.onBackground)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun ShowQRDialog(
|
|
||||||
user: User,
|
|
||||||
accountViewModel: AccountViewModel,
|
|
||||||
onScan: (Route) -> Unit,
|
|
||||||
onClose: () -> Unit,
|
|
||||||
) {
|
|
||||||
var presenting by remember { mutableStateOf(true) }
|
|
||||||
|
|
||||||
Dialog(
|
|
||||||
onDismissRequest = onClose,
|
|
||||||
properties = DialogProperties(usePlatformDefaultWidth = false),
|
|
||||||
) {
|
|
||||||
SetDialogToEdgeToEdge()
|
|
||||||
|
|
||||||
Scaffold(
|
|
||||||
topBar = {
|
|
||||||
TopAppBar(
|
|
||||||
title = {},
|
|
||||||
navigationIcon = {
|
|
||||||
Row {
|
|
||||||
Spacer(modifier = StdHorzSpacer)
|
|
||||||
BackButton(onPress = onClose)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
colors =
|
|
||||||
TopAppBarDefaults.topAppBarColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
) { pad ->
|
|
||||||
Surface(
|
|
||||||
Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(
|
|
||||||
start = 10.dp,
|
|
||||||
end = 10.dp,
|
|
||||||
top = pad.calculateTopPadding(),
|
|
||||||
bottom = pad.calculateBottomPadding(),
|
|
||||||
).consumeWindowInsets(pad)
|
|
||||||
.imePadding(),
|
|
||||||
) {
|
|
||||||
Column {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(horizontal = 10.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
) {
|
|
||||||
if (presenting) {
|
|
||||||
Column {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
) {
|
|
||||||
RobohashFallbackAsyncImage(
|
|
||||||
robot = user.pubkeyHex,
|
|
||||||
model = user.profilePicture(),
|
|
||||||
contentDescription = stringRes(R.string.profile_image),
|
|
||||||
modifier = MaterialTheme.colorScheme.largeProfilePictureModifier,
|
|
||||||
loadProfilePicture = accountViewModel.settings.showProfilePictures(),
|
|
||||||
loadRobohash = accountViewModel.settings.isNotPerformanceMode(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
|
|
||||||
) {
|
|
||||||
CreateTextWithEmoji(
|
|
||||||
text = user.info?.bestName() ?: "",
|
|
||||||
tags = user.info?.tags,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
fontSize = 20.sp,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
|
|
||||||
) {
|
|
||||||
val nip05 = user.nip05()
|
|
||||||
if (nip05 != null) {
|
|
||||||
val nip05Verified =
|
|
||||||
nip05VerificationAsAState(user.info!!, user.pubkeyHex, accountViewModel)
|
|
||||||
|
|
||||||
DisplayNIP05(nip05, nip05Verified, accountViewModel)
|
|
||||||
} else {
|
|
||||||
Text(
|
|
||||||
text = user.pubkeyDisplayHex(),
|
|
||||||
fontSize = Font14SP,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = Size10dp),
|
|
||||||
) {
|
|
||||||
QrCodeDrawer(user.toNostrUri())
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(modifier = Modifier.padding(horizontal = 30.dp)) {
|
|
||||||
FilledTonalButton(
|
|
||||||
onClick = { presenting = false },
|
|
||||||
shape = RoundedCornerShape(Size35dp),
|
|
||||||
modifier = Modifier.fillMaxWidth().height(50.dp),
|
|
||||||
) {
|
|
||||||
Text(text = stringRes(R.string.scan_qr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NIP19QrCodeScanner(accountViewModel) {
|
|
||||||
if (it == null) {
|
|
||||||
presenting = true
|
|
||||||
} else {
|
|
||||||
onScan(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+245
@@ -0,0 +1,245 @@
|
|||||||
|
/**
|
||||||
|
* 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.qrcode
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.imePadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FilledTonalButton
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.model.User
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.DisplayNIP05
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.nip05VerificationAsAState
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.navs.EmptyNav
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.LoadUser
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.largeProfilePictureModifier
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun ShowQRScreenPreview() {
|
||||||
|
val accountViewModel = mockAccountViewModel()
|
||||||
|
accountViewModel.userProfile().info =
|
||||||
|
UserMetadata().apply {
|
||||||
|
name = "My Name"
|
||||||
|
picture = "Picture"
|
||||||
|
nip05 = null
|
||||||
|
banner = "http://banner.com/test"
|
||||||
|
website = "http://mywebsite.com/test"
|
||||||
|
about = "This is the about me"
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowQRScreen(
|
||||||
|
pubkey = accountViewModel.userProfile().pubkeyHex,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = EmptyNav(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BackButton(onPress: () -> Unit) {
|
||||||
|
IconButton(
|
||||||
|
onClick = onPress,
|
||||||
|
) {
|
||||||
|
ArrowBackIcon(MaterialTheme.colorScheme.onBackground)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ShowQRScreen(
|
||||||
|
pubkey: HexKey,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
) {
|
||||||
|
LoadUser(pubkey, accountViewModel) { user ->
|
||||||
|
if (user != null) {
|
||||||
|
ShowQRScreen(
|
||||||
|
user = user,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ShowQRScreen(
|
||||||
|
user: User,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
) {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopBarWithBackButton(
|
||||||
|
"",
|
||||||
|
nav::popBack,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ShowQRBody(
|
||||||
|
user: User,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: INav,
|
||||||
|
) {
|
||||||
|
var presenting by remember { mutableStateOf(true) }
|
||||||
|
if (presenting) {
|
||||||
|
PresentQR(user, accountViewModel) {
|
||||||
|
presenting = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NIP19QrCodeScanner(accountViewModel) {
|
||||||
|
if (it == null) {
|
||||||
|
presenting = true
|
||||||
|
} else {
|
||||||
|
nav.nav(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PresentQR(
|
||||||
|
user: User,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
switchToScan: () -> Unit,
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
RobohashFallbackAsyncImage(
|
||||||
|
robot = user.pubkeyHex,
|
||||||
|
model = user.profilePicture(),
|
||||||
|
contentDescription = stringRes(R.string.profile_image),
|
||||||
|
modifier = MaterialTheme.colorScheme.largeProfilePictureModifier,
|
||||||
|
loadProfilePicture = accountViewModel.settings.showProfilePictures(),
|
||||||
|
loadRobohash = accountViewModel.settings.isNotPerformanceMode(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
|
||||||
|
) {
|
||||||
|
CreateTextWithEmoji(
|
||||||
|
text = user.info?.bestName() ?: "",
|
||||||
|
tags = user.info?.tags,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
|
||||||
|
) {
|
||||||
|
val nip05 = user.nip05()
|
||||||
|
if (nip05 != null) {
|
||||||
|
val nip05Verified =
|
||||||
|
nip05VerificationAsAState(user.info!!, user.pubkeyHex, accountViewModel)
|
||||||
|
|
||||||
|
DisplayNIP05(nip05, nip05Verified, accountViewModel)
|
||||||
|
} else {
|
||||||
|
Text(
|
||||||
|
text = user.pubkeyDisplayHex(),
|
||||||
|
fontSize = Font14SP,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(horizontal = Size10dp),
|
||||||
|
) {
|
||||||
|
QrCodeDrawer(user.toNostrUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(modifier = Modifier.padding(horizontal = 30.dp)) {
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = switchToScan,
|
||||||
|
shape = RoundedCornerShape(Size35dp),
|
||||||
|
modifier = Modifier.fillMaxWidth().height(50.dp),
|
||||||
|
) {
|
||||||
|
Text(text = stringRes(R.string.scan_qr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user