Creates a Loading screen to get events before redirecting them to the appropriate screen (DMs, Channels, Threads)

This commit is contained in:
Vitor Pamplona
2023-03-24 09:58:28 -04:00
parent e910260375
commit eed62c36b9
10 changed files with 122 additions and 2 deletions
@@ -7,7 +7,7 @@ import java.util.regex.Pattern
object Nip19 { object Nip19 {
enum class Type { enum class Type {
USER, NOTE, RELAY, ADDRESS USER, NOTE, EVENT, RELAY, ADDRESS
} }
val nip19regex = Pattern.compile("(nostr:)?@?(nsec1|npub1|nevent1|naddr1|note1|nprofile1|nrelay1)([qpzry9x8gf2tvdw0s3jn54khce6mua7l]+)(.*)", Pattern.CASE_INSENSITIVE) val nip19regex = Pattern.compile("(nostr:)?@?(nsec1|npub1|nevent1|naddr1|note1|nprofile1|nrelay1)([qpzry9x8gf2tvdw0s3jn54khce6mua7l]+)(.*)", Pattern.CASE_INSENSITIVE)
@@ -76,7 +76,7 @@ object Nip19 {
?.get(0) ?.get(0)
?.toString(Charsets.UTF_8) ?.toString(Charsets.UTF_8)
return Return(Type.USER, hex, relay) return Return(Type.EVENT, hex, relay)
} }
private fun nrelay(bytes: ByteArray): Return? { private fun nrelay(bytes: ByteArray): Return? {
@@ -37,6 +37,8 @@ class MainActivity : FragmentActivity() {
val startingPage = when (nip19?.type) { val startingPage = when (nip19?.type) {
Nip19.Type.USER -> "User/${nip19.hex}" Nip19.Type.USER -> "User/${nip19.hex}"
Nip19.Type.NOTE -> "Note/${nip19.hex}" Nip19.Type.NOTE -> "Note/${nip19.hex}"
Nip19.Type.EVENT -> "Event/${nip19.hex}"
Nip19.Type.ADDRESS -> "Note/${nip19.hex}"
else -> null else -> null
} }
@@ -87,6 +87,8 @@ class NewPostViewModel : ViewModel() {
addUserToMentions(LocalCache.getOrCreateUser(results.key.hex)) addUserToMentions(LocalCache.getOrCreateUser(results.key.hex))
} else if (results?.key?.type == Nip19.Type.NOTE) { } else if (results?.key?.type == Nip19.Type.NOTE) {
addNoteToReplyTos(LocalCache.getOrCreateNote(results.key.hex)) addNoteToReplyTos(LocalCache.getOrCreateNote(results.key.hex))
} else if (results?.key?.type == Nip19.Type.EVENT) {
addNoteToReplyTos(LocalCache.getOrCreateNote(results.key.hex))
} else if (results?.key?.type == Nip19.Type.ADDRESS) { } else if (results?.key?.type == Nip19.Type.ADDRESS) {
val note = LocalCache.checkGetOrCreateAddressableNote(results.key.hex) val note = LocalCache.checkGetOrCreateAddressableNote(results.key.hex)
if (note != null) { if (note != null) {
@@ -107,6 +109,10 @@ class NewPostViewModel : ViewModel() {
} else if (results?.key?.type == Nip19.Type.NOTE) { } else if (results?.key?.type == Nip19.Type.NOTE) {
val note = LocalCache.getOrCreateNote(results.key.hex) val note = LocalCache.getOrCreateNote(results.key.hex)
"#[${tagIndex(note)}]${results.restOfWord}"
} else if (results?.key?.type == Nip19.Type.EVENT) {
val note = LocalCache.getOrCreateNote(results.key.hex)
"#[${tagIndex(note)}]${results.restOfWord}" "#[${tagIndex(note)}]${results.restOfWord}"
} else if (results?.key?.type == Nip19.Type.ADDRESS) { } else if (results?.key?.type == Nip19.Type.ADDRESS) {
val note = LocalCache.checkGetOrCreateAddressableNote(results.key.hex) val note = LocalCache.checkGetOrCreateAddressableNote(results.key.hex)
@@ -12,6 +12,7 @@ import androidx.compose.ui.text.withStyle
import androidx.navigation.NavController import androidx.navigation.NavController
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
import com.vitorpamplona.amethyst.service.nip19.Nip19 import com.vitorpamplona.amethyst.service.nip19.Nip19
@Composable @Composable
@@ -47,11 +48,28 @@ fun ClickableRoute(
if (note.event is ChannelCreateEvent) { if (note.event is ChannelCreateEvent) {
CreateClickableText(note.idDisplayNote(), nip19.additionalChars, "Channel/${nip19.hex}", navController) CreateClickableText(note.idDisplayNote(), nip19.additionalChars, "Channel/${nip19.hex}", navController)
} else if (note.event is PrivateDmEvent) {
CreateClickableText(note.idDisplayNote(), nip19.additionalChars, "Room/${note.author?.pubkeyHex}", navController)
} else if (channel != null) { } else if (channel != null) {
CreateClickableText(channel.toBestDisplayName(), nip19.additionalChars, "Channel/${note.channel()?.idHex}", navController) CreateClickableText(channel.toBestDisplayName(), nip19.additionalChars, "Channel/${note.channel()?.idHex}", navController)
} else { } else {
CreateClickableText(note.idDisplayNote(), nip19.additionalChars, "Note/${nip19.hex}", navController) CreateClickableText(note.idDisplayNote(), nip19.additionalChars, "Note/${nip19.hex}", navController)
} }
} else if (nip19.type == Nip19.Type.EVENT) {
val noteBase = LocalCache.getOrCreateNote(nip19.hex)
val noteState by noteBase.live().metadata.observeAsState()
val note = noteState?.note ?: return
val channel = note.channel()
if (note.event is ChannelCreateEvent) {
CreateClickableText(note.idDisplayNote(), nip19.additionalChars, "Channel/${nip19.hex}", navController)
} else if (note.event is PrivateDmEvent) {
CreateClickableText(note.idDisplayNote(), nip19.additionalChars, "Room/${note.author?.pubkeyHex}", navController)
} else if (channel != null) {
CreateClickableText(channel.toBestDisplayName(), nip19.additionalChars, "Channel/${note.channel()?.idHex}", navController)
} else {
CreateClickableText(note.idDisplayNote(), nip19.additionalChars, "Event/${nip19.hex}", navController)
}
} else { } else {
Text( Text(
"@${nip19.hex}${nip19.additionalChars} " "@${nip19.hex}${nip19.additionalChars} "
@@ -23,6 +23,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChatroomScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HashtagScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.HashtagScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HiddenUsersScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.HiddenUsersScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.HomeScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.HomeScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.LoadRedirectScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.NotificationScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.NotificationScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ProfileScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.ProfileScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchScreen
@@ -126,6 +127,16 @@ fun AppNavigation(
) )
}) })
} }
Route.Event.let { route ->
composable(route.route, route.arguments, content = {
LoadRedirectScreen(
eventId = it.arguments?.getString("id"),
accountViewModel = accountViewModel,
navController = navController
)
})
}
} }
if (nextPage != null) { if (nextPage != null) {
@@ -87,6 +87,12 @@ sealed class Route(
icon = R.drawable.ic_moments, icon = R.drawable.ic_moments,
arguments = listOf(navArgument("id") { type = NavType.StringType }) arguments = listOf(navArgument("id") { type = NavType.StringType })
) )
object Event : Route(
route = "Event/{id}",
icon = R.drawable.ic_moments,
arguments = listOf(navArgument("id") { type = NavType.StringType })
)
} }
// ** // **
@@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.ui.qrcode package com.vitorpamplona.amethyst.ui.qrcode
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
@@ -19,6 +20,8 @@ fun NIP19QrCodeScanner(onScan: (String?) -> Unit) {
val startingPage = when (nip19?.type) { val startingPage = when (nip19?.type) {
Nip19.Type.USER -> "User/${nip19.hex}" Nip19.Type.USER -> "User/${nip19.hex}"
Nip19.Type.NOTE -> "Note/${nip19.hex}" Nip19.Type.NOTE -> "Note/${nip19.hex}"
Nip19.Type.EVENT -> "Event/${nip19.hex}"
Nip19.Type.ADDRESS -> "Note/${nip19.hex}"
else -> null else -> null
} }
@@ -28,6 +31,7 @@ fun NIP19QrCodeScanner(onScan: (String?) -> Unit) {
onScan(null) onScan(null)
} }
} catch (e: Throwable) { } catch (e: Throwable) {
Log.e("NIP19 Scanner", "Error parsing $it", e)
// QR can be anything, do not throw errors. // QR can be anything, do not throw errors.
onScan(null) onScan(null)
} }
@@ -0,0 +1,63 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
@Composable
fun LoadRedirectScreen(eventId: String?, accountViewModel: AccountViewModel, navController: NavController) {
if (eventId == null) return
val baseNote = LocalCache.checkGetOrCreateNote(eventId) ?: return
val noteState by baseNote.live().metadata.observeAsState()
val note = noteState?.note
LaunchedEffect(key1 = noteState) {
val event = note?.event
val channel = note?.channel()
if (event == null) {
// stay here, loading
} else if (event is ChannelCreateEvent) {
navController.backQueue.removeLast()
navController.navigate("Channel/${note.idHex}")
} else if (event is PrivateDmEvent) {
navController.backQueue.removeLast()
navController.navigate("Room/${note.author?.pubkeyHex}")
} else if (channel != null) {
navController.backQueue.removeLast()
navController.navigate("Channel/${channel.idHex}")
} else {
navController.backQueue.removeLast()
navController.navigate("Note/${note.idHex}")
}
}
Column(
Modifier
.fillMaxHeight()
.fillMaxWidth()
.padding(horizontal = 50.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(stringResource(R.string.looking_for_event, eventId))
}
}
+2
View File
@@ -263,4 +263,6 @@
<string name="wallet_connect_service_relay">Wallet Connect Relay</string> <string name="wallet_connect_service_relay">Wallet Connect Relay</string>
<string name="pledge_amount_in_sats">Pledge Amount in Sats</string> <string name="pledge_amount_in_sats">Pledge Amount in Sats</string>
<string name="looking_for_event">"Looking for Event %1$s"</string>
</resources> </resources>
@@ -69,4 +69,12 @@ class NIP19ParserTest {
assertEquals("30023:46fcbe3065eaf1ae7811465924e48923363ff3f526bd6f73d7c184b16bd8ce4d:1679509418", result?.hex) assertEquals("30023:46fcbe3065eaf1ae7811465924e48923363ff3f526bd6f73d7c184b16bd8ce4d:1679509418", result?.hex)
assertEquals(null, result?.relay) assertEquals(null, result?.relay)
} }
@Test
fun nEventParserTest() {
val result = Nip19.uriToRoute("nostr:nevent1qqs0tsw8hjacs4fppgdg7f5yhgwwfkyua4xcs3re9wwkpkk2qeu6mhql22rcy")
assertEquals(Nip19.Type.EVENT, result?.type)
assertEquals("f5c1c7bcbb8855210a1a8f2684ba1ce4d89ced4d8844792b9d60daca0679addc", result?.hex)
assertEquals(null, result?.relay)
}
} }