diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1e59bc909f..6839f39cd7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -22,10 +22,16 @@
android:theme="@style/Theme.Amethyst">
-
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt
new file mode 100644
index 0000000000..f186ceb743
--- /dev/null
+++ b/app/src/main/java/com/vitorpamplona/amethyst/service/Nip19.kt
@@ -0,0 +1,62 @@
+package com.vitorpamplona.amethyst.service
+
+import com.vitorpamplona.amethyst.model.toHexKey
+import nostr.postr.bechToBytes
+
+class Nip19 {
+
+ enum class Type {
+ USER, NOTE
+ }
+ data class Return(val type: Type, val hex: String)
+
+ fun uriToRoute(uri: String?): Return? {
+ try {
+ val key = uri?.removePrefix("nostr:")
+
+ if (key != null) {
+ val bytes = key.bechToBytes()
+ if (key.startsWith("npub")) {
+ return Return(Type.USER, bytes.toHexKey())
+ }
+ if (key.startsWith("note")) {
+ return Return(Type.NOTE, bytes.toHexKey())
+ }
+ if (key.startsWith("nprofile")) {
+ val tlv = parseTLV(bytes)
+ val hex = tlv.get(0)?.get(0)?.toHexKey()
+ if (hex != null)
+ return Return(Type.USER, hex)
+ }
+ if (key.startsWith("nevent")) {
+ val tlv = parseTLV(bytes)
+ val hex = tlv.get(0)?.get(0)?.toHexKey()
+ if (hex != null)
+ return Return(Type.USER, hex)
+ }
+ }
+ } catch (e: Throwable) {
+ e.printStackTrace()
+ }
+
+ return null
+ }
+
+ fun parseTLV(data: ByteArray): Map> {
+ var result = mutableMapOf>()
+ var rest = data
+ while (rest.isNotEmpty()) {
+ val t = rest[0]
+ val l = rest[1]
+ val v = rest.sliceArray(IntRange(2, (2 + l) - 1))
+ rest = rest.sliceArray(IntRange(2 + l, rest.size-1))
+ if (v.size < l) continue
+
+ if (!result.containsKey(t)) {
+ result.put(t, mutableListOf())
+ }
+ result.get(t)?.add(v)
+ }
+ return result
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrThreadDataSource.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrThreadDataSource.kt
index c7066c43f0..81cd6bb03e 100644
--- a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrThreadDataSource.kt
+++ b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrThreadDataSource.kt
@@ -57,20 +57,25 @@ object NostrThreadDataSource: NostrDataSource("SingleThreadFeed") {
}
fun loadThread(noteId: String) {
- val note = LocalCache.notes[noteId] ?: return
+ val note = LocalCache.notes[noteId]
- val thread = mutableListOf()
- val threadSet = mutableSetOf()
+ if (note != null) {
+ val thread = mutableListOf()
+ val threadSet = mutableSetOf()
- val threadRoot = note.replyTo?.firstOrNull() ?: note
+ val threadRoot = note.replyTo?.firstOrNull() ?: note
- loadDown(threadRoot, thread, threadSet)
+ loadDown(threadRoot, thread, threadSet)
- // Currently orders by date of each event, descending, at each level of the reply stack
- val order = compareByDescending { it.replyLevelSignature() }
+ // Currently orders by date of each event, descending, at each level of the reply stack
+ val order = compareByDescending { it.replyLevelSignature() }
- eventsToWatch.clear()
- eventsToWatch.addAll(thread.sortedWith(order).map { it.idHex })
+ eventsToWatch.clear()
+ eventsToWatch.addAll(thread.sortedWith(order).map { it.idHex })
+ } else {
+ eventsToWatch.clear()
+ eventsToWatch.add(noteId)
+ }
resetFilters()
}
diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrUserProfileDataSource.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrUserProfileDataSource.kt
index 94f37508b5..dccdb91e39 100644
--- a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrUserProfileDataSource.kt
+++ b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrUserProfileDataSource.kt
@@ -4,6 +4,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
+import com.vitorpamplona.amethyst.model.toByteArray
import nostr.postr.JsonFilter
import nostr.postr.events.MetadataEvent
import nostr.postr.events.TextNoteEvent
@@ -13,7 +14,7 @@ object NostrUserProfileDataSource: NostrDataSource("UserProfileFeed") {
var user: User? = null
fun loadUserProfile(userId: String) {
- user = LocalCache.users[userId]
+ user = LocalCache.getOrCreateUser(userId.toByteArray())
resetFilters()
}
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 6c113cfc32..b6a4e8ad89 100644
--- a/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt
+++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt
@@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.ui
+import android.net.Uri
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import androidx.activity.ComponentActivity
@@ -17,15 +18,28 @@ import coil.decode.SvgDecoder
import com.vitorpamplona.amethyst.EncryptedStorage
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.ServiceManager
+import com.vitorpamplona.amethyst.model.decodePublicKey
+import com.vitorpamplona.amethyst.model.toHexKey
+import com.vitorpamplona.amethyst.service.Nip19
import com.vitorpamplona.amethyst.service.relays.Client
import com.vitorpamplona.amethyst.ui.screen.AccountScreen
import com.vitorpamplona.amethyst.ui.screen.AccountStateViewModel
import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
+import fr.acinq.secp256k1.Hex
+import nostr.postr.Persona
+import nostr.postr.bechToBytes
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ val nip19 = Nip19().uriToRoute(intent?.data?.toString())
+ val startingPage = when (nip19?.type) {
+ Nip19.Type.USER -> "User/${nip19.hex}"
+ Nip19.Type.NOTE -> "Note/${nip19.hex}"
+ else -> null
+ }
+
Coil.setImageLoader {
ImageLoader.Builder(this).components {
if (SDK_INT >= 28) {
@@ -39,6 +53,7 @@ class MainActivity : ComponentActivity() {
.build()
}
+
setContent {
AmethystTheme {
// A surface container using the 'background' color from the theme
@@ -48,7 +63,7 @@ class MainActivity : ComponentActivity() {
AccountStateViewModel(LocalPreferences(applicationContext))
}
- AccountScreen(accountViewModel)
+ AccountScreen(accountViewModel, startingPage)
}
}
}
@@ -67,4 +82,4 @@ class MainActivity : ComponentActivity() {
super.onPause()
}
-}
\ No newline at end of file
+}
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 8db00572c6..1d97af2a97 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
@@ -11,11 +11,16 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
fun AppNavigation(
navController: NavHostController,
accountViewModel: AccountViewModel,
- accountStateViewModel: AccountStateViewModel
+ accountStateViewModel: AccountStateViewModel,
+ nextPage: String? = null
) {
NavHost(navController, startDestination = Route.Home.route) {
Routes.forEach {
composable(it.route, it.arguments, content = it.buildScreen(accountViewModel, accountStateViewModel, navController))
}
}
+
+ if (nextPage != null) {
+ navController.navigate(nextPage)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt
index f12d7f04f4..c39834823f 100644
--- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt
+++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/AccountScreen.kt
@@ -8,7 +8,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
-fun AccountScreen(accountStateViewModel: AccountStateViewModel) {
+fun AccountScreen(accountStateViewModel: AccountStateViewModel, startingPage: String?) {
val accountState by accountStateViewModel.accountContent.collectAsStateWithLifecycle()
Column() {
@@ -18,10 +18,10 @@ fun AccountScreen(accountStateViewModel: AccountStateViewModel) {
LoginPage(accountStateViewModel)
}
is AccountState.LoggedIn -> {
- MainScreen(AccountViewModel(state.account), accountStateViewModel)
+ MainScreen(AccountViewModel(state.account), accountStateViewModel, startingPage)
}
is AccountState.LoggedInViewOnly -> {
- MainScreen(AccountViewModel(state.account), accountStateViewModel)
+ MainScreen(AccountViewModel(state.account), accountStateViewModel, startingPage)
}
}
}
diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt
index 4a0dd486e0..b13fd024e5 100644
--- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt
+++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/MainScreen.kt
@@ -27,7 +27,7 @@ import com.vitorpamplona.amethyst.ui.navigation.currentRoute
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
-fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel) {
+fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: AccountStateViewModel, startingPage: String? = null) {
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
@@ -50,7 +50,7 @@ fun MainScreen(accountViewModel: AccountViewModel, accountStateViewModel: Accoun
scaffoldState = scaffoldState
) {
Column(modifier = Modifier.padding(bottom = it.calculateBottomPadding())) {
- AppNavigation(navController, accountViewModel, accountStateViewModel)
+ AppNavigation(navController, accountViewModel, accountStateViewModel, startingPage)
}
}
}