diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c22a472ac..518e6a539 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,6 +43,13 @@ + + + + + + + diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt index 3afecdcb7..49823b3e4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt @@ -21,6 +21,8 @@ import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.ServiceManager import com.vitorpamplona.amethyst.service.nip19.Nip19 import com.vitorpamplona.amethyst.service.relays.Client +import com.vitorpamplona.amethyst.ui.navigation.Route +import com.vitorpamplona.amethyst.ui.note.Nip47 import com.vitorpamplona.amethyst.ui.screen.AccountScreen import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel import com.vitorpamplona.amethyst.ui.theme.AmethystTheme @@ -28,6 +30,8 @@ import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import java.net.URLEncoder +import java.nio.charset.StandardCharsets class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -40,6 +44,14 @@ class MainActivity : FragmentActivity() { Nip19.Type.EVENT -> "Event/${nip19.hex}" Nip19.Type.ADDRESS -> "Note/${nip19.hex}" else -> null + } ?: try { + intent?.data?.toString()?.let { + Nip47.parse(it) + val encodedUri = URLEncoder.encode(it, StandardCharsets.UTF_8.toString()) + Route.Home.base + "?nip47=" + encodedUri + } + } catch (e: Exception) { + null } Coil.setImageLoader { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt index bba99be7a..d7418272c 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt @@ -1,8 +1,12 @@ package com.vitorpamplona.amethyst.ui.navigation import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost @@ -39,6 +43,7 @@ fun AppNavigation( nextPage: String? = null ) { val homePagerState = rememberPagerState() + var actionableNextPage by remember { mutableStateOf(nextPage) } // Avoids creating ViewModels for performance reasons (up to 1 second delays) val accountState by accountViewModel.accountLiveData.observeAsState() @@ -76,8 +81,9 @@ fun AppNavigation( } Route.Home.let { route -> - composable(route.route, route.arguments, content = { + composable(route.route, route.arguments, content = { it -> val scrollToTop = it.arguments?.getBoolean("scrollToTop") ?: false + val nip47 = it.arguments?.getString("nip47") HomeScreen( homeFeedViewModel = homeFeedViewModel, @@ -85,13 +91,17 @@ fun AppNavigation( accountViewModel = accountViewModel, navController = navController, pagerState = homePagerState, - scrollToTop = scrollToTop + scrollToTop = scrollToTop, + nip47 = nip47 ) // Avoids running scroll to top when back button is pressed if (scrollToTop) { it.arguments?.remove("scrollToTop") } + if (nip47 != null) { + it.arguments?.remove("nip47") + } }) } @@ -178,7 +188,10 @@ fun AppNavigation( } } - if (nextPage != null) { - navController.navigate(nextPage) + actionableNextPage?.let { + LaunchedEffect(it) { + navController.navigate(it) + } + actionableNextPage = null } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt index 66fd2fa05..ddbe4d307 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt @@ -24,9 +24,12 @@ sealed class Route( get() = route.substringBefore("?") object Home : Route( - route = "Home?scrollToTop={scrollToTop}", + route = "Home?scrollToTop={scrollToTop}&nip47={nip47}", icon = R.drawable.ic_home, - arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }), + arguments = listOf( + navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }, + navArgument("nip47") { type = NavType.StringType; nullable = true; defaultValue = null } + ), hasNewItems = { accountViewModel, cache -> homeHasNewItems(accountViewModel, cache) } ) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt index 8315f59b5..7b0899c6b 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/UpdateZapAmountDialog.kt @@ -49,6 +49,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -165,17 +166,40 @@ class UpdateZapAmountViewModel : ViewModel() { walletConnectSecret.text != (account?.zapPaymentRequest?.secret ?: "") ) } + + fun updateNIP47(uri: String) { + val contact = Nip47.parse(uri) + if (contact != null) { + walletConnectPubkey = + TextFieldValue(contact.pubKeyHex) + walletConnectRelay = + TextFieldValue(contact.relayUri ?: "") + walletConnectSecret = + TextFieldValue(contact.secret ?: "") + } + } } @OptIn(ExperimentalLayoutApi::class) @Composable -fun UpdateZapAmountDialog(onClose: () -> Unit, account: Account) { +fun UpdateZapAmountDialog(onClose: () -> Unit, account: Account, nip47uri: String? = null) { val context = LocalContext.current val scope = rememberCoroutineScope() val postViewModel: UpdateZapAmountViewModel = viewModel() + val uri = LocalUriHandler.current LaunchedEffect(account) { postViewModel.load(account) + if (nip47uri != null) { + try { + postViewModel.updateNIP47(nip47uri) + } catch (e: IllegalArgumentException) { + scope.launch { + Toast.makeText(context, e.message, Toast.LENGTH_SHORT) + .show() + } + } + } } Dialog( @@ -299,6 +323,18 @@ fun UpdateZapAmountDialog(onClose: () -> Unit, account: Account) { stringResource(id = R.string.wallet_connect_service), Modifier.weight(1f) ) + + IconButton(onClick = { + runCatching { uri.openUri("https://nwc.getalby.com/apps/new?c=Amethyst") } + }) { + Icon( + painter = painterResource(R.drawable.alby), + null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified + ) + } + IconButton(onClick = { qrScanning = true }) { @@ -330,15 +366,7 @@ fun UpdateZapAmountDialog(onClose: () -> Unit, account: Account) { qrScanning = false if (!it.isNullOrEmpty()) { try { - val contact = Nip47.parse(it) - if (contact != null) { - postViewModel.walletConnectPubkey = - TextFieldValue(contact.pubKeyHex) - postViewModel.walletConnectRelay = - TextFieldValue(contact.relayUri ?: "") - postViewModel.walletConnectSecret = - TextFieldValue(contact.secret ?: "") - } + postViewModel.updateNIP47(it) } catch (e: IllegalArgumentException) { scope.launch { Toast.makeText(context, e.message, Toast.LENGTH_SHORT) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt index 15676ba3c..c191de3aa 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HomeScreen.kt @@ -11,7 +11,11 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect 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.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource @@ -28,6 +32,7 @@ import com.vitorpamplona.amethyst.service.NostrHomeDataSource import com.vitorpamplona.amethyst.ui.dal.HomeConversationsFeedFilter import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter import com.vitorpamplona.amethyst.ui.navigation.Route +import com.vitorpamplona.amethyst.ui.note.UpdateZapAmountDialog import com.vitorpamplona.amethyst.ui.screen.FeedView import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel @@ -42,10 +47,12 @@ fun HomeScreen( accountViewModel: AccountViewModel, navController: NavController, pagerState: PagerState, - scrollToTop: Boolean = false + scrollToTop: Boolean = false, + nip47: String? = null ) { val coroutineScope = rememberCoroutineScope() val account = accountViewModel.accountLiveData.value?.account ?: return + var wantsToAddNip47 by remember { mutableStateOf(nip47) } LaunchedEffect(accountViewModel) { HomeNewThreadFeedFilter.account = account @@ -55,6 +62,10 @@ fun HomeScreen( repliesFeedViewModel.invalidateData() } + if (wantsToAddNip47 != null) { + UpdateZapAmountDialog({ wantsToAddNip47 = null }, account = account, wantsToAddNip47) + } + val lifeCycleOwner = LocalLifecycleOwner.current DisposableEffect(accountViewModel) { val observer = LifecycleEventObserver { _, event -> diff --git a/app/src/main/res/drawable/alby.xml b/app/src/main/res/drawable/alby.xml index 1188a62fc..cb036c35e 100644 --- a/app/src/main/res/drawable/alby.xml +++ b/app/src/main/res/drawable/alby.xml @@ -1,39 +1,58 @@ + android:width="489dp" + android:height="489dp" + android:viewportWidth="489" + android:viewportHeight="489"> + android:pathData="M89.58,181.77C96.83,182.51 103.84,181.66 110.26,179.49L110.42,179.63L110.66,179.87C83.35,210.92 64.09,251.1 56.14,295.56C50.5,327.03 67.39,357.29 95.35,370.61C140.92,392.29 191.9,404.42 245.64,404.42C299.39,404.42 349.32,392.54 394.53,371.27C422.59,358.07 439.6,327.82 434.06,296.31C426.2,251.6 406.52,211.48 379.48,179.96C345.33,140.15 379.58,179.8 379.58,179.8C379.58,179.8 391.32,182.34 397.62,181.9C421.93,180.22 441.52,160.37 442.89,136.03C444.51,106.88 420.52,82.9 391.36,84.54C367.36,85.89 347.63,105.01 345.58,128.97C344.95,136.44 346.01,143.62 348.41,150.15L348.02,150.54L347.79,150.77C318.32,129.03 283.17,116.22 245.05,116.22C206.93,116.22 171.54,129.12 141.99,151.01L141.77,150.78L141.4,150.42L140.69,149.71C143.01,143.22 144.01,136.11 143.33,128.71C141.16,104.88 121.47,85.9 97.57,84.55C68.36,82.92 44.37,106.91 46.01,136.09C47.34,159.78 65.97,179.34 89.56,181.78L89.58,181.77Z" + android:strokeWidth="19.962" + android:fillColor="#ffffff" + android:strokeColor="#ffffff"/> - - - + + + + + + + +