Update to make use of lightning-kmp-appp

This commit is contained in:
Kgothatso Ngako committed 2026-08-02 20:43:30 +02:00
1 parent e9510c4ea5
commit 2f06485ec5
11 files changed
+66 -40

No files matched your search

@@ -7,13 +7,14 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import fr.acinq.phoenix.NFCActivity
import press.mantra.compose.PlatformContext import press.mantra.compose.PlatformContext
import press.mantra.compose.MantraApp import press.mantra.compose.MantraApp
import press.mantra.compose.MantraGlobal import press.mantra.compose.MantraGlobal
import fr.acinq.phoenix.PhoenixGlobal import fr.acinq.phoenix.PhoenixGlobal
import fr.acinq.phoenix.android.services.HceService import fr.acinq.phoenix.android.services.HceService
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity(), NFCActivity {
private var nfcAdapter: NfcAdapter? = null private var nfcAdapter: NfcAdapter? = null
@@ -41,11 +42,11 @@ class MainActivity : ComponentActivity() {
} }
} }
fun stopHceService() { override fun stopHceService() {
stopService(Intent(this@MainActivity, HceService::class.java)) stopService(Intent(this@MainActivity, HceService::class.java))
} }
fun stopNfcReader() { override fun stopNfcReader() {
if (com.machankura.compose.ui.composable.widgets.nfc.NfcStateRepository.isReading()) { if (com.machankura.compose.ui.composable.widgets.nfc.NfcStateRepository.isReading()) {
com.machankura.compose.ui.composable.widgets.nfc.NfcStateRepository.updateState( com.machankura.compose.ui.composable.widgets.nfc.NfcStateRepository.updateState(
com.machankura.compose.ui.composable.widgets.nfc.NfcState.Inactive) com.machankura.compose.ui.composable.widgets.nfc.NfcState.Inactive)
@@ -7,22 +7,31 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import fr.acinq.phoenix.PhoenixGlobal import fr.acinq.phoenix.PhoenixGlobal
import fr.acinq.phoenix.android.BusinessManager import fr.acinq.phoenix.android.BusinessManager
import fr.acinq.phoenix.android.LightningApplication
import fr.acinq.phoenix.utils.PlatformContext import fr.acinq.phoenix.utils.PlatformContext
import fr.acinq.phoenix.utils.preferences.GlobalPrefs import fr.acinq.phoenix.utils.preferences.GlobalPrefs
/** This datastore persists preferences across node ids. */ /** This datastore persists preferences across node ids. */
val Context.globalPrefs: DataStore<Preferences> by preferencesDataStore(name = "globalprefs") val Context.globalPrefs: DataStore<Preferences> by preferencesDataStore(name = "globalprefs")
class MantraApplication: Application() { class MantraApplication: Application(), LightningApplication {
lateinit var globalPrefs: GlobalPrefs private lateinit var globalPrefs: GlobalPrefs
lateinit var phoenixGlobal: PhoenixGlobal private lateinit var phoenixGlobal: PhoenixGlobal
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
phoenixGlobal = PhoenixGlobal(PlatformContext(applicationContext)) phoenixGlobal = PhoenixGlobal(PlatformContext(applicationContext))
globalPrefs = GlobalPrefs(applicationContext.globalPrefs) globalPrefs = GlobalPrefs(applicationContext.globalPrefs)
BusinessManager.initialize(applicationContext) BusinessManager.initialize(this)
} }
override fun getGlobalPrefs(): GlobalPrefs {
return globalPrefs
}
override fun getPhoenixGlobal(): PhoenixGlobal {
return phoenixGlobal
}
} }
@@ -24,10 +24,10 @@ actual fun schedulePlatformLogic(phoenixGlobal: PhoenixGlobal) {
} }
actual fun getShowIntroFlow(phoenixGlobal: PhoenixGlobal): Flow<Boolean> { actual fun getShowIntroFlow(phoenixGlobal: PhoenixGlobal): Flow<Boolean> {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication
return machankuraApplication.globalPrefs.getShowIntro return machankuraApplication.getGlobalPrefs().getShowIntro
} }
actual fun getGlobalPrefs(phoenixGlobal: PhoenixGlobal): GlobalPrefs { actual fun getGlobalPrefs(phoenixGlobal: PhoenixGlobal): GlobalPrefs {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication
return machankuraApplication.globalPrefs return machankuraApplication.getGlobalPrefs()
} }
@@ -36,12 +36,12 @@ actual fun loadAndDecryptSeed(phoenixGlobal: PhoenixGlobal): DecryptSeedResult {
actual fun getAvailableWalletsMeta(phoenixGlobal: PhoenixGlobal): Flow<Map<WalletId, UserWalletMetadata>> { actual fun getAvailableWalletsMeta(phoenixGlobal: PhoenixGlobal): Flow<Map<WalletId, UserWalletMetadata>> {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication
return machankuraApplication.globalPrefs.getAvailableWalletsMeta return machankuraApplication.getGlobalPrefs().getAvailableWalletsMeta
} }
actual suspend fun saveAvailableWalletMeta(phoenixGlobal: PhoenixGlobal, metadata: UserWalletMetadata) { actual suspend fun saveAvailableWalletMeta(phoenixGlobal: PhoenixGlobal, metadata: UserWalletMetadata) {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication
machankuraApplication.globalPrefs.saveAvailableWalletMeta(metadata) machankuraApplication.getGlobalPrefs().saveAvailableWalletMeta(metadata)
} }
actual suspend fun saveAvailableWalletMeta( actual suspend fun saveAvailableWalletMeta(
@@ -52,7 +52,7 @@ actual suspend fun saveAvailableWalletMeta(
isHidden: Boolean isHidden: Boolean
) { ) {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication val machankuraApplication = phoenixGlobal.ctx.applicationContext as MantraApplication
machankuraApplication.globalPrefs.saveAvailableWalletMeta( machankuraApplication.getGlobalPrefs().saveAvailableWalletMeta(
walletId = walletId, walletId = walletId,
name = name, name = name,
avatar = avatar, avatar = avatar,
@@ -361,7 +361,7 @@ abstract class NostrDao(
// MLSGroupEvent handling... // MLSGroupEvent handling...
nostrEvent.toKeyPackageEvent()?.let { keyPackageEvent -> nostrEvent.toKeyPackageEvent()?.let { keyPackageEvent ->
logger.d("KeyPackageEvent: $keyPackageEvent") logger.d("KeyPackageEvent: ${keyPackageEvent.toJson()}")
database.marmotKeyPackageDao().upsert( database.marmotKeyPackageDao().upsert(
MarmotKeyPackage( MarmotKeyPackage(
id = keyPackageEvent.id, id = keyPackageEvent.id,
@@ -7,23 +7,24 @@ import androidx.room3.Transaction
import androidx.room3.Upsert import androidx.room3.Upsert
import press.mantra.compose.database.model.intermdiate.LocalAccount import press.mantra.compose.database.model.intermdiate.LocalAccount
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import press.mantra.compose.database.model.UnsignedNostrEvent
@Dao @Dao
interface UnsignedNostrEventDao { interface UnsignedNostrEventDao {
@Query("SELECT * FROM UnsignedNostrEvent") @Query("SELECT * FROM UnsignedNostrEvent")
fun getAUnsignedNostrEvents(): List<press.mantra.compose.database.model.UnsignedNostrEvent> fun getAUnsignedNostrEvents(): List<UnsignedNostrEvent>
@Query("SELECT * FROM UnsignedNostrEvent") // TODO: where nostrEvent.unsignedNostrEventId is 0 @Query("SELECT * FROM UnsignedNostrEvent") // TODO: where nostrEvent.unsignedNostrEventId is 0
fun getAllUnprocessedUnsignedNostrEvents(): List<press.mantra.compose.database.model.UnsignedNostrEvent> fun getAllUnprocessedUnsignedNostrEvents(): List<UnsignedNostrEvent>
@Query("SELECT * FROM UnsignedNostrEvent WHERE id = :id") @Query("SELECT * FROM UnsignedNostrEvent WHERE id = :id")
fun getUnsignedNostrEventById(id: Long): Flow<press.mantra.compose.database.model.UnsignedNostrEvent?> fun getUnsignedNostrEventById(id: Long): Flow<UnsignedNostrEvent?>
@Upsert @Upsert
suspend fun upsert(unsignedNostrEvent: press.mantra.compose.database.model.UnsignedNostrEvent): Long suspend fun upsert(unsignedNostrEvent: UnsignedNostrEvent): Long
@Insert @Insert
suspend fun insert(unsignedNostrEvents: List<press.mantra.compose.database.model.UnsignedNostrEvent>) suspend fun insert(unsignedNostrEvents: List<UnsignedNostrEvent>)
@Transaction @Transaction
@Query("SELECT * FROM UnsignedNostrEvent WHERE kind = 0 AND pubKey = :publicKey") @Query("SELECT * FROM UnsignedNostrEvent WHERE kind = 0 AND pubKey = :publicKey")
@@ -33,6 +34,6 @@ interface UnsignedNostrEventDao {
@Query("SELECT * FROM UnsignedNostrEvent WHERE kind = 0") @Query("SELECT * FROM UnsignedNostrEvent WHERE kind = 0")
fun getLocalAccounts(): List<LocalAccount> fun getLocalAccounts(): List<LocalAccount>
@Query("SELECT * FROM UnsignedNostrEvent WHERE pubKey = :publicKey AND signedAt IS NULL") @Query("SELECT * FROM UnsignedNostrEvent WHERE pubKey = :publicKey AND signedAt IS NULL ORDER BY kind ASC")
fun observeUnsignedNostrEvents(publicKey: String): Flow<press.mantra.compose.database.model.UnsignedNostrEvent?> fun observeUnsignedNostrEvents(publicKey: String): Flow<UnsignedNostrEvent?>
} }
@@ -0,0 +1,12 @@
package press.mantra.compose.extensions
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import fr.acinq.lightning.crypto.LocalKeyManager
import fr.acinq.phoenix.managers.nostrPrivateKey
fun LocalKeyManager.nostrPublicKey(): String {
return KeyPair(
privKey = nostrPrivateKey().value.toByteArray()
).pubKey.toHexKey()
}
@@ -8,7 +8,6 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toRelay.ReqCmd
import com.vitorpamplona.quartz.nip77Negentropy.NegCloseCmd import com.vitorpamplona.quartz.nip77Negentropy.NegCloseCmd
import com.vitorpamplona.quartz.nip77Negentropy.NegOpenCmd import com.vitorpamplona.quartz.nip77Negentropy.NegOpenCmd
import fr.acinq.phoenix.data.ActiveWallet import fr.acinq.phoenix.data.ActiveWallet
import fr.acinq.phoenix.managers.nostrPublicKey
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -20,6 +19,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import press.mantra.compose.extensions.nostrPublicKey
/** /**
@@ -17,7 +17,6 @@ import fr.acinq.bitcoin.byteVector
import fr.acinq.lightning.Lightning import fr.acinq.lightning.Lightning
import fr.acinq.lightning.crypto.LocalKeyManager import fr.acinq.lightning.crypto.LocalKeyManager
import fr.acinq.phoenix.managers.NodeParamsManager import fr.acinq.phoenix.managers.NodeParamsManager
import fr.acinq.phoenix.managers.nostrPublicKey
import fr.acinq.phoenix.managers.nsecPassword import fr.acinq.phoenix.managers.nsecPassword
import fr.acinq.phoenix.utils.MnemonicLanguage import fr.acinq.phoenix.utils.MnemonicLanguage
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
@@ -25,6 +24,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO import kotlinx.coroutines.IO
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import press.mantra.compose.extensions.nostrPublicKey
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
class CreateProfileViewModel( class CreateProfileViewModel(
@@ -129,11 +129,6 @@ class CreateProfileViewModel(
biography = createProfileFormState.biographyField.textFieldState.text.toString(), biography = createProfileFormState.biographyField.textFieldState.text.toString(),
onCompletion = { onCompletion = {
logger.d("Created: $pubkey" ) logger.d("Created: $pubkey" )
marmotRepository.publishMarmotKeyPackageBundle(
publicKey = pubkey,
nsecPassword = localKeyManager.nsecPassword()
)
} }
) )
} }
@@ -9,7 +9,6 @@ import press.mantra.compose.ui.composable.navigation.routes.SovereignWalletStart
import press.mantra.compose.ui.view.state.NavigationUIState import press.mantra.compose.ui.view.state.NavigationUIState
import co.touchlab.kermit.Logger import co.touchlab.kermit.Logger
import fr.acinq.phoenix.data.ActiveWallet import fr.acinq.phoenix.data.ActiveWallet
import fr.acinq.phoenix.managers.nostrPublicKey
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO import kotlinx.coroutines.IO
@@ -21,6 +20,7 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import press.mantra.compose.extensions.nostrPublicKey
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
class NavigationViewModel( class NavigationViewModel(
@@ -181,24 +181,26 @@ class NavigationViewModel(
NavigationUIState.StartupPhoenix NavigationUIState.StartupPhoenix
} }
} else { } else {
if (activeWallet.business == null) { activeWallet.business.let { business ->
_navigationUIState.getAndUpdate { if (business == null) {
NavigationUIState.StartupPhoenix
}
} else {
val activeUserPublicKey = activeWallet.business.walletManager.keyManager.value?.nostrPublicKey()
if (activeUserPublicKey == null) {
_navigationUIState.getAndUpdate { _navigationUIState.getAndUpdate {
NavigationUIState.Loading(text = "Couldn't get the nostrKey") NavigationUIState.StartupPhoenix
} }
} else { } else {
logger.i("Observing: $activeUserPublicKey") val activeUserPublicKey = business.walletManager.keyManager.value?.nostrPublicKey()
loadNostrProfile(activeUserPublicKey) if (activeUserPublicKey == null) {
_navigationUIState.getAndUpdate {
NavigationUIState.Loading(text = "Couldn't get the nostrKey")
}
} else {
logger.i("Observing: $activeUserPublicKey")
loadNostrProfile(activeUserPublicKey)
}
} }
logger.i("Navigation UI State is Landing")
} }
logger.i("Navigation UI State is Landing")
} }
} }
} }
@@ -18,6 +18,7 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
@@ -26,6 +27,7 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import press.mantra.compose.database.model.NostrEvent import press.mantra.compose.database.model.NostrEvent
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Instant import kotlin.time.Instant
class NotaryViewModel( class NotaryViewModel(
@@ -204,8 +206,12 @@ class NotaryViewModel(
marmotRepository.observeActiveMarmotKeyPackageBundle( marmotRepository.observeActiveMarmotKeyPackageBundle(
publicKey = keyPair.pubKey.toHexKey() publicKey = keyPair.pubKey.toHexKey()
).distinctUntilChanged().collect { marmotKeyPackageBundleOrNull -> ).distinctUntilChanged().collect { marmotKeyPackageBundleOrNull ->
if (marmotKeyPackageBundleOrNull == null) { if (marmotKeyPackageBundleOrNull == null) {
guardNotarization("key package bundle for ${keyPair.pubKey.toHexKey()}") { guardNotarization("key package bundle for ${keyPair.pubKey.toHexKey()}") {
delay(3.seconds)
// TODO: We shouldn't try to create a key package without profile
// we should be check profile in the query to get updates
logger.d("Need to create new key package bundle") logger.d("Need to create new key package bundle")
marmotRepository.publishMarmotKeyPackageBundle( marmotRepository.publishMarmotKeyPackageBundle(
publicKey = keyPair.pubKey.toHexKey(), publicKey = keyPair.pubKey.toHexKey(),