New Cashu Redeeming card.
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.components
|
package com.vitorpamplona.amethyst.ui.components
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@@ -12,7 +14,9 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -22,7 +26,6 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -35,21 +38,30 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextDirection
|
import androidx.compose.ui.text.style.TextDirection
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.core.content.ContextCompat.startActivity
|
import androidx.core.content.ContextCompat.startActivity
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.fasterxml.jackson.databind.node.TextNode
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.model.ThemeType
|
||||||
import com.vitorpamplona.amethyst.service.CashuProcessor
|
import com.vitorpamplona.amethyst.service.CashuProcessor
|
||||||
import com.vitorpamplona.amethyst.service.CashuToken
|
import com.vitorpamplona.amethyst.service.CashuToken
|
||||||
import com.vitorpamplona.amethyst.ui.actions.LoadingAnimation
|
import com.vitorpamplona.amethyst.ui.actions.LoadingAnimation
|
||||||
import com.vitorpamplona.amethyst.ui.note.CashuIcon
|
import com.vitorpamplona.amethyst.ui.note.CashuIcon
|
||||||
import com.vitorpamplona.amethyst.ui.note.CopyIcon
|
import com.vitorpamplona.amethyst.ui.note.CopyIcon
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.OpenInNewIcon
|
||||||
import com.vitorpamplona.amethyst.ui.note.ZapIcon
|
import com.vitorpamplona.amethyst.ui.note.ZapIcon
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size18Modifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
import com.vitorpamplona.amethyst.ui.theme.Size20dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.SmallishBorder
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
|
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -84,13 +96,45 @@ fun CashuPreview(cashutoken: String, accountViewModel: AccountViewModel) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CashuPreview(token: CashuToken, accountViewModel: AccountViewModel) {
|
fun CashuPreview(token: CashuToken, accountViewModel: AccountViewModel) {
|
||||||
val lud16 = remember(accountViewModel) {
|
CashuPreviewNew(token, accountViewModel::meltCashu, accountViewModel::toast)
|
||||||
accountViewModel.account.userProfile().info?.lud16
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val useWebService = false
|
@Composable
|
||||||
|
@Preview()
|
||||||
|
fun CashuPreviewPreview() {
|
||||||
|
val sharedPreferencesViewModel: SharedPreferencesViewModel = viewModel()
|
||||||
|
|
||||||
|
sharedPreferencesViewModel.init()
|
||||||
|
sharedPreferencesViewModel.updateTheme(ThemeType.DARK)
|
||||||
|
|
||||||
|
AmethystTheme(sharedPrefsViewModel = sharedPreferencesViewModel) {
|
||||||
|
Column() {
|
||||||
|
CashuPreview(
|
||||||
|
token = CashuToken("token", "mint", 32400, TextNode("")),
|
||||||
|
melt = { token, context, onDone ->
|
||||||
|
},
|
||||||
|
toast = { title, message ->
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
CashuPreviewNew(
|
||||||
|
token = CashuToken("token", "mint", 32400, TextNode("")),
|
||||||
|
melt = { token, context, onDone ->
|
||||||
|
},
|
||||||
|
toast = { title, message ->
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CashuPreview(
|
||||||
|
token: CashuToken,
|
||||||
|
melt: (CashuToken, Context, (String, String) -> Unit) -> Unit,
|
||||||
|
toast: (String, String) -> Unit
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val clipboardManager = LocalClipboardManager.current
|
val clipboardManager = LocalClipboardManager.current
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
@@ -148,28 +192,10 @@ fun CashuPreview(token: CashuToken, accountViewModel: AccountViewModel) {
|
|||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (lud16 != null) {
|
isRedeeming = true
|
||||||
scope.launch(Dispatchers.IO) {
|
melt(token, context) { title, message ->
|
||||||
isRedeeming = true
|
toast(title, message)
|
||||||
CashuProcessor().melt(
|
isRedeeming = false
|
||||||
token,
|
|
||||||
lud16,
|
|
||||||
onSuccess = { title, message ->
|
|
||||||
isRedeeming = false
|
|
||||||
accountViewModel.toast(title, message)
|
|
||||||
},
|
|
||||||
onError = { title, message ->
|
|
||||||
isRedeeming = false
|
|
||||||
accountViewModel.toast(title, message)
|
|
||||||
},
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
accountViewModel.toast(
|
|
||||||
context.getString(R.string.no_lightning_address_set),
|
|
||||||
context.getString(R.string.user_x_does_not_have_a_lightning_address_setup_to_receive_sats, accountViewModel.account.userProfile().toBestDisplayName())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shape = QuoteBorder,
|
shape = QuoteBorder,
|
||||||
@@ -196,12 +222,12 @@ fun CashuPreview(token: CashuToken, accountViewModel: AccountViewModel) {
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
try {
|
try {
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("cashu://$token"))
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("cashu://${token.token}"))
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
|
||||||
startActivity(context, intent, null)
|
startActivity(context, intent, null)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
accountViewModel.toast("Cashu", context.getString(R.string.cashu_no_wallet_found))
|
toast("Cashu", context.getString(R.string.cashu_no_wallet_found))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
shape = QuoteBorder,
|
shape = QuoteBorder,
|
||||||
@@ -232,3 +258,111 @@ fun CashuPreview(token: CashuToken, accountViewModel: AccountViewModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CashuPreviewNew(
|
||||||
|
token: CashuToken,
|
||||||
|
melt: (CashuToken, Context, (String, String) -> Unit) -> Unit,
|
||||||
|
toast: (String, String) -> Unit
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val clipboardManager = LocalClipboardManager.current
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 10.dp, end = 10.dp, top = 10.dp, bottom = 10.dp)
|
||||||
|
.clip(shape = QuoteBorder)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(10.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(R.drawable.cashu),
|
||||||
|
null,
|
||||||
|
modifier = Modifier.size(13.dp),
|
||||||
|
tint = Color.Unspecified
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.cashu),
|
||||||
|
fontSize = 12.sp,
|
||||||
|
modifier = Modifier.padding(start = 5.dp, bottom = 1.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "${token.totalAmount} ${stringResource(id = R.string.sats)}",
|
||||||
|
fontSize = 20.sp
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(modifier = Modifier.padding(top = 5.dp)) {
|
||||||
|
var isRedeeming by remember {
|
||||||
|
mutableStateOf(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = {
|
||||||
|
isRedeeming = true
|
||||||
|
melt(token, context) { title, message ->
|
||||||
|
toast(title, message)
|
||||||
|
isRedeeming = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = SmallishBorder
|
||||||
|
) {
|
||||||
|
if (isRedeeming) {
|
||||||
|
LoadingAnimation()
|
||||||
|
} else {
|
||||||
|
ZapIcon(Size20dp, tint = MaterialTheme.colorScheme.onBackground)
|
||||||
|
}
|
||||||
|
Spacer(StdHorzSpacer)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"Redeem",
|
||||||
|
color = MaterialTheme.colorScheme.onBackground,
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = {
|
||||||
|
try {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("cashu://${token.token}"))
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
|
||||||
|
startActivity(context, intent, null)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
toast("Cashu", context.getString(R.string.cashu_no_wallet_found))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shape = SmallishBorder,
|
||||||
|
contentPadding = PaddingValues(0.dp)
|
||||||
|
) {
|
||||||
|
OpenInNewIcon(Size18Modifier, tint = MaterialTheme.colorScheme.onBackground)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = StdHorzSpacer)
|
||||||
|
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = {
|
||||||
|
// Copying the token to clipboard
|
||||||
|
clipboardManager.setText(AnnotatedString(token.token))
|
||||||
|
},
|
||||||
|
shape = SmallishBorder,
|
||||||
|
contentPadding = PaddingValues(0.dp)
|
||||||
|
) {
|
||||||
|
CopyIcon(Size18Modifier, tint = MaterialTheme.colorScheme.onBackground)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import androidx.compose.material.icons.filled.ExpandLess
|
|||||||
import androidx.compose.material.icons.filled.ExpandMore
|
import androidx.compose.material.icons.filled.ExpandMore
|
||||||
import androidx.compose.material.icons.filled.Link
|
import androidx.compose.material.icons.filled.Link
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
|
import androidx.compose.material.icons.filled.OpenInNew
|
||||||
import androidx.compose.material.icons.filled.PushPin
|
import androidx.compose.material.icons.filled.PushPin
|
||||||
import androidx.compose.material.icons.filled.Report
|
import androidx.compose.material.icons.filled.Report
|
||||||
import androidx.compose.material.icons.filled.VolumeOff
|
import androidx.compose.material.icons.filled.VolumeOff
|
||||||
@@ -196,6 +197,16 @@ fun CopyIcon(modifier: Modifier, tint: Color = Color.Unspecified) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun OpenInNewIcon(modifier: Modifier, tint: Color = Color.Unspecified) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.OpenInNew,
|
||||||
|
stringResource(id = R.string.copy_to_clipboard),
|
||||||
|
tint = tint,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ExpandLessIcon(modifier: Modifier) {
|
fun ExpandLessIcon(modifier: Modifier) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import com.vitorpamplona.amethyst.model.RelayInformation
|
|||||||
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
|
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.model.UserState
|
import com.vitorpamplona.amethyst.model.UserState
|
||||||
|
import com.vitorpamplona.amethyst.service.CashuProcessor
|
||||||
|
import com.vitorpamplona.amethyst.service.CashuToken
|
||||||
import com.vitorpamplona.amethyst.service.HttpClient
|
import com.vitorpamplona.amethyst.service.HttpClient
|
||||||
import com.vitorpamplona.amethyst.service.Nip05Verifier
|
import com.vitorpamplona.amethyst.service.Nip05Verifier
|
||||||
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
|
||||||
@@ -1047,6 +1049,34 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
|
|||||||
account.dismissPaymentRequest(request)
|
account.dismissPaymentRequest(request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun meltCashu(
|
||||||
|
token: CashuToken,
|
||||||
|
context: Context,
|
||||||
|
onDone: (String, String) -> Unit
|
||||||
|
) {
|
||||||
|
val lud16 = account.userProfile().info?.lud16
|
||||||
|
if (lud16 != null) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
CashuProcessor().melt(
|
||||||
|
token,
|
||||||
|
lud16,
|
||||||
|
onSuccess = { title, message ->
|
||||||
|
onDone(title, message)
|
||||||
|
},
|
||||||
|
onError = { title, message ->
|
||||||
|
onDone(title, message)
|
||||||
|
},
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onDone(
|
||||||
|
context.getString(R.string.no_lightning_address_set),
|
||||||
|
context.getString(R.string.user_x_does_not_have_a_lightning_address_setup_to_receive_sats, account.userProfile().toBestDisplayName())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HasNotificationDot(bottomNavigationItems: ImmutableList<Route>) {
|
class HasNotificationDot(bottomNavigationItems: ImmutableList<Route>) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ val BottomTopHeight = Modifier.height(50.dp)
|
|||||||
val TabRowHeight = Modifier
|
val TabRowHeight = Modifier
|
||||||
|
|
||||||
val SmallBorder = RoundedCornerShape(7.dp)
|
val SmallBorder = RoundedCornerShape(7.dp)
|
||||||
|
val SmallishBorder = RoundedCornerShape(9.dp)
|
||||||
val QuoteBorder = RoundedCornerShape(15.dp)
|
val QuoteBorder = RoundedCornerShape(15.dp)
|
||||||
val ButtonBorder = RoundedCornerShape(20.dp)
|
val ButtonBorder = RoundedCornerShape(20.dp)
|
||||||
val EditFieldBorder = RoundedCornerShape(25.dp)
|
val EditFieldBorder = RoundedCornerShape(25.dp)
|
||||||
|
|||||||
@@ -37,17 +37,17 @@ import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
|||||||
private val DarkColorPalette = darkColorScheme(
|
private val DarkColorPalette = darkColorScheme(
|
||||||
primary = Purple200,
|
primary = Purple200,
|
||||||
secondary = Teal200,
|
secondary = Teal200,
|
||||||
// secondary = Purple700,
|
|
||||||
tertiary = Teal200,
|
tertiary = Teal200,
|
||||||
background = Color(0xFF000000),
|
background = Color(0xFF000000),
|
||||||
surface = Color(0xFF000000)
|
surface = Color(0xFF000000),
|
||||||
|
surfaceVariant = Color(red = 29, green = 26, blue = 34)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val LightColorPalette = lightColorScheme(
|
private val LightColorPalette = lightColorScheme(
|
||||||
primary = Purple500,
|
primary = Purple500,
|
||||||
secondary = Teal200,
|
secondary = Teal200,
|
||||||
// secondary = Purple700,
|
tertiary = Teal200,
|
||||||
tertiary = Teal200
|
surfaceVariant = Color(red = 250, green = 245, blue = 252)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val DarkNewItemBackground = DarkColorPalette.primary.copy(0.12f)
|
private val DarkNewItemBackground = DarkColorPalette.primary.copy(0.12f)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user