Fork Aux to make Torch

This commit is contained in:
Kgothatso Ngako
2026-06-17 18:10:06 +03:00
parent 3df0b9ba4c
commit bad1b0eb31
343 changed files with 2747 additions and 3382 deletions

View File

@@ -119,14 +119,14 @@ kotlin {
}
android {
namespace = "ac.cord.auxiliary.android"
namespace = "at.torch.android"
compileSdk = libs.versions.android.compileSdk.get().toInt()
buildFeatures {
buildConfig = true
}
defaultConfig {
applicationId = "ac.cord.auxiliary.android"
applicationId = "at.torch.android"
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
@@ -187,11 +187,11 @@ sqldelight {
compose.desktop {
application {
mainClass = "ac.cord.auxiliary.desktop.MainKt"
mainClass = "at.torch.desktop.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "ac.cord.auxiliary.desktop"
packageName = "at.torch.desktop"
packageVersion = "1.0.0"
}
}

View File

@@ -7,7 +7,7 @@
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".InomboloApplication"
android:name=".TorchApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@@ -16,7 +16,7 @@
android:theme="@android:style/Theme.Material.Light.NoActionBar">
<activity
android:exported="true"
android:name="ac.cord.auxiliary.android.MainActivity">
android:name="at.torch.android.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -1,8 +1,8 @@
package ac.cord.auxiliary.android
package at.torch.android
import ac.cord.auxiliary.compose.AuxApp
import ac.cord.auxiliary.compose.AuxGlobal
import ac.cord.auxiliary.compose.PlatformContext
import at.torch.auxiliary.compose.AuxApp
import at.torch.auxiliary.compose.AuxGlobal
import at.torch.compose.PlatformContext
import android.content.Intent
import android.nfc.NfcAdapter
import android.os.Bundle
@@ -48,8 +48,9 @@ class MainActivity : ComponentActivity() {
}
fun stopNfcReader() {
if (NfcStateRepository.isReading()) {
NfcStateRepository.updateState(NfcState.Inactive)
if (_root_ide_package_.com.machankura.compose.ui.composable.widgets.nfc.NfcStateRepository.isReading()) {
_root_ide_package_.com.machankura.compose.ui.composable.widgets.nfc.NfcStateRepository.updateState(
_root_ide_package_.com.machankura.compose.ui.composable.widgets.nfc.NfcState.Inactive)
}
nfcAdapter?.disableReaderMode(this@MainActivity)
}

View File

@@ -1,4 +1,4 @@
package ac.cord.auxiliary.android
package at.torch.android
import android.app.Application
import android.content.Context
@@ -7,14 +7,13 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import fr.acinq.phoenix.PhoenixGlobal
import fr.acinq.phoenix.android.BusinessManager
import fr.acinq.phoenix.utils.Logging
import fr.acinq.phoenix.utils.PlatformContext
import fr.acinq.phoenix.utils.preferences.GlobalPrefs
/** This datastore persists preferences across node ids. */
val Context.globalPrefs: DataStore<Preferences> by preferencesDataStore(name = "globalprefs")
class InomboloApplication: Application() {
class TorchApplication: Application() {
lateinit var globalPrefs: GlobalPrefs
lateinit var phoenixGlobal: PhoenixGlobal

View File

@@ -1,6 +1,6 @@
package ac.cord.auxiliary.compose
package at.torch.compose
import ac.cord.auxiliary.android.BuildConfig
import at.torch.auxiliary.android.BuildConfig
actual object AppVersion {
actual val versionName: String

View File

@@ -1,4 +1,4 @@
package ac.cord.auxiliary.compose
package at.torch.compose
import android.os.Build
@@ -6,4 +6,4 @@ class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"
}
actual fun getPlatform(): Platform = AndroidPlatform()
actual fun getPlatform(): Platform = _root_ide_package_.at.torch.compose.AndroidPlatform()

View File

@@ -1,4 +1,4 @@
package ac.cord.auxiliary.compose
package at.torch.compose
import android.content.Context

View File

@@ -1,22 +1,22 @@
package ac.cord.auxiliary.compose.database.builder
package at.torch.compose.database.builder
import ac.cord.auxiliary.compose.PlatformContext
import ac.cord.auxiliary.compose.database.AuxDatabase
import at.torch.compose.PlatformContext
import at.torch.compose.database.TorchDatabase
import androidx.room3.Room
import androidx.room3.RoomDatabase
actual object PlatformDatabaseBuilder {
actual fun getDatabaseBuilder(platformContext: PlatformContext): RoomDatabase.Builder<AuxDatabase> {
actual fun getDatabaseBuilder(platformContext: PlatformContext): RoomDatabase.Builder<TorchDatabase> {
val appContext = platformContext.applicationContext
val dbFile = appContext.getDatabasePath("aux.db")
return Room.databaseBuilder<AuxDatabase>(
return Room.databaseBuilder<TorchDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}
actual fun getInMemoryDatabaseBuilder(platform: PlatformContext): RoomDatabase.Builder<AuxDatabase> {
actual fun getInMemoryDatabaseBuilder(platform: PlatformContext): RoomDatabase.Builder<TorchDatabase> {
return Room.inMemoryDatabaseBuilder(
context = platform.applicationContext
)

View File

@@ -1,6 +1,6 @@
package ac.cord.auxiliary.compose.extensions
package at.torch.compose.extensions
import ac.cord.auxiliary.android.InomboloApplication
import at.torch.android.TorchApplication
import fr.acinq.phoenix.PhoenixGlobal
import fr.acinq.phoenix.android.BusinessManager
import fr.acinq.phoenix.android.services.ChannelsWatcher
@@ -23,11 +23,11 @@ actual fun schedulePlatformLogic(phoenixGlobal: PhoenixGlobal) {
ContactsPhotoCleaner.schedule(phoenixGlobal.ctx.applicationContext)
}
actual fun getShowIntroFlow(phoenixGlobal: PhoenixGlobal): Flow<Boolean> {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as InomboloApplication
val machankuraApplication = phoenixGlobal.ctx.applicationContext as TorchApplication
return machankuraApplication.globalPrefs.getShowIntro
}
actual fun getGlobalPrefs(phoenixGlobal: PhoenixGlobal): GlobalPrefs {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as InomboloApplication
val machankuraApplication = phoenixGlobal.ctx.applicationContext as TorchApplication
return machankuraApplication.globalPrefs
}

View File

@@ -1,4 +1,4 @@
package ac.cord.auxiliary.compose.ui.theme
package at.torch.compose.ui.theme
import android.os.Build
import androidx.compose.material3.ColorScheme

View File

@@ -1,7 +1,7 @@
package ac.cord.auxiliary.compose.ui.view.model
package at.torch.compose.ui.view.model
import ac.cord.auxiliary.android.InomboloApplication
import ac.cord.auxiliary.compose.AppVersion
import at.torch.android.TorchApplication
import at.torch.auxiliary.compose.AppVersion
import co.touchlab.kermit.Logger
import fr.acinq.bitcoin.MnemonicCode
import fr.acinq.lightning.crypto.LocalKeyManager
@@ -35,12 +35,12 @@ actual fun loadAndDecryptSeed(phoenixGlobal: PhoenixGlobal): DecryptSeedResult {
}
actual fun getAvailableWalletsMeta(phoenixGlobal: PhoenixGlobal): Flow<Map<WalletId, UserWalletMetadata>> {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as InomboloApplication
val machankuraApplication = phoenixGlobal.ctx.applicationContext as TorchApplication
return machankuraApplication.globalPrefs.getAvailableWalletsMeta
}
actual suspend fun saveAvailableWalletMeta(phoenixGlobal: PhoenixGlobal, metadata: UserWalletMetadata) {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as InomboloApplication
val machankuraApplication = phoenixGlobal.ctx.applicationContext as TorchApplication
machankuraApplication.globalPrefs.saveAvailableWalletMeta(metadata)
}
@@ -51,7 +51,7 @@ actual suspend fun saveAvailableWalletMeta(
avatar: String,
isHidden: Boolean
) {
val machankuraApplication = phoenixGlobal.ctx.applicationContext as InomboloApplication
val machankuraApplication = phoenixGlobal.ctx.applicationContext as TorchApplication
machankuraApplication.globalPrefs.saveAvailableWalletMeta(
walletId = walletId,
name = name,

View File

@@ -16,9 +16,9 @@
package fr.acinq.phoenix.android
import ac.cord.auxiliary.android.InomboloApplication
import ac.cord.auxiliary.compose.AppVersion
import ac.cord.auxiliary.compose.ui.composable.widgets.wallet.WalletAvatars
import at.torch.android.TorchApplication
import at.torch.auxiliary.compose.AppVersion
import at.torch.auxiliary.compose.ui.composable.widgets.wallet.WalletAvatars
import android.content.Context
import android.text.format.DateUtils
import fr.acinq.lightning.LiquidityEvents
@@ -34,10 +34,8 @@ import fr.acinq.phoenix.data.StartupParams
import fr.acinq.phoenix.data.WalletId
import fr.acinq.phoenix.data.inFlightPaymentsCount
import fr.acinq.phoenix.managers.AppConnectionsDaemon
import fr.acinq.phoenix.managers.DataStoreManager
import fr.acinq.phoenix.managers.NodeParamsManager
import fr.acinq.phoenix.managers.PeerManager
import fr.acinq.phoenix.managers.WalletManager
import fr.acinq.phoenix.managers.global.CurrencyManager
import fr.acinq.phoenix.utils.MnemonicLanguage
import fr.acinq.phoenix.utils.SystemNotificationHelper
@@ -47,7 +45,6 @@ import fr.acinq.phoenix.utils.preferences.UserWalletMetadata
import fr.acinq.phoenix.utils.preferences.getByWalletIdOrDefault
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@@ -70,7 +67,7 @@ object BusinessManager {
// No memory leaks because this can only contain the application context.
private lateinit var appContext: Context
private val application by lazy { appContext as InomboloApplication }
private val application by lazy { appContext as TorchApplication }
/** A map of (walletId -> active businesses) */
private val _businessFlow = MutableStateFlow<Map<WalletId, BusinessRunning>>(emptyMap())

View File

@@ -16,7 +16,7 @@
package fr.acinq.phoenix.android.services
import ac.cord.auxiliary.android.BuildConfig
import at.torch.auxiliary.android.BuildConfig
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy

View File

@@ -16,7 +16,7 @@
package fr.acinq.phoenix.android.services
import ac.cord.auxiliary.android.BuildConfig
import at.torch.auxiliary.android.BuildConfig
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy

View File

@@ -16,7 +16,7 @@
package fr.acinq.phoenix.android.services
import ac.cord.auxiliary.android.BuildConfig
import at.torch.auxiliary.android.BuildConfig
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy

View File

@@ -16,8 +16,8 @@
package fr.acinq.phoenix.android.services
import ac.cord.auxiliary.android.BuildConfig
import ac.cord.auxiliary.android.InomboloApplication
import at.torch.auxiliary.android.BuildConfig
import at.torch.android.TorchApplication
import android.content.Context
import android.os.Build
import androidx.work.CoroutineWorker
@@ -95,7 +95,7 @@ class InflightPaymentsWatcher(context: Context, workerParams: WorkerParameters)
}
val watchResult = userWallets.map { (walletId, wallet) ->
val walletMetadata = (applicationContext as InomboloApplication).globalPrefs.getAvailableWalletsMeta.first().getByWalletIdOrDefault(walletId)
val walletMetadata = (applicationContext as TorchApplication).globalPrefs.getAvailableWalletsMeta.first().getByWalletIdOrDefault(walletId)
watchWallet(walletId, walletMetadata, wallet.words)
}

View File

@@ -1,16 +1,12 @@
package fr.acinq.phoenix.android.services
import ac.cord.auxiliary.android.BuildConfig
import ac.cord.auxiliary.android.InomboloApplication
import android.app.Notification
import at.torch.auxiliary.android.BuildConfig
import at.torch.android.TorchApplication
import android.app.Service
import android.content.Intent
import android.content.pm.ServiceInfo
import android.os.Build
import android.os.Handler
import android.os.Looper
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.ServiceCompat
import fr.acinq.lightning.utils.Connection
import fr.acinq.phoenix.PhoenixBusiness
import fr.acinq.phoenix.PhoenixGlobal
@@ -104,7 +100,7 @@ class PaymentsForegroundService : Service() {
is DecryptSeedResult.Failure -> {
log.info("unable to read seed, ignoring background message (reason=$reason)")
serviceScope.launch {
val walletMetadataMap = (application as InomboloApplication).globalPrefs.getAvailableWalletsMeta.first()
val walletMetadataMap = (application as TorchApplication).globalPrefs.getAvailableWalletsMeta.first()
val metadata = walletMetadataMap.getByWalletIdOrDefault(walletId)
when (reason) {
"IncomingPayment" -> SystemNotificationHelper.notifyPaymentMissedAppUnavailable(applicationContext, metadata)

View File

@@ -1,6 +1,6 @@
package fr.acinq.phoenix.utils
import ac.cord.auxiliary.android.BuildConfig
import at.torch.auxiliary.android.BuildConfig
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory

View File

@@ -16,12 +16,11 @@
package fr.acinq.phoenix.utils
import ac.cord.auxiliary.android.BuildConfig
import ac.cord.auxiliary.android.MainActivity
import ac.cord.auxiliary.android.R
import at.torch.auxiliary.android.BuildConfig
import at.torch.android.MainActivity
import at.torch.auxiliary.android.R
import android.Manifest
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context

View File

@@ -16,7 +16,7 @@
package fr.acinq.phoenix.utils.extensions
import ac.cord.auxiliary.android.MainActivity
import at.torch.android.MainActivity
import android.content.Context
import android.content.ContextWrapper

View File

@@ -1,7 +0,0 @@
package ac.cord.auxiliary.compose
class AuxGlobal(
val platformContext: PlatformContext
) {
}

View File

@@ -1,7 +0,0 @@
package ac.cord.auxiliary.compose
interface Platform {
val name: String
}
expect fun getPlatform(): Platform

View File

@@ -1,3 +0,0 @@
package ac.cord.auxiliary.compose
expect class PlatformContext

View File

@@ -1,176 +0,0 @@
package ac.cord.auxiliary.compose.database
import ac.cord.auxiliary.compose.database.converters.AuxConverters
import ac.cord.auxiliary.compose.database.dao.BroadcastNostrEventReceiptDao
import ac.cord.auxiliary.compose.database.dao.BroadcastNostrEventRequestDao
import ac.cord.auxiliary.compose.database.dao.ChatMessageDao
import ac.cord.auxiliary.compose.database.dao.ChatMessageBroadcastNostrEventRequestRelationDao
import ac.cord.auxiliary.compose.database.dao.ChatMessageBroadcastNostrEventReceiptRelationDao
import ac.cord.auxiliary.compose.database.dao.ChatMessageNostrEventRelationDao
import ac.cord.auxiliary.compose.database.dao.ChatRoomDao
import ac.cord.auxiliary.compose.database.dao.ConnectionDao
import ac.cord.auxiliary.compose.database.dao.GiftWrapMessageDao
import ac.cord.auxiliary.compose.database.dao.GiftWrapSealDao
import ac.cord.auxiliary.compose.database.dao.GiftWrapPayloadDao
import ac.cord.auxiliary.compose.database.dao.InReplyToRelationDao
import ac.cord.auxiliary.compose.database.dao.MentionDao
import ac.cord.auxiliary.compose.database.dao.NegentropySynchronizeRequestDao
import ac.cord.auxiliary.compose.database.dao.NegentropySynchronizeResultDao
import ac.cord.auxiliary.compose.database.dao.NostrDao
import ac.cord.auxiliary.compose.database.dao.NostrEventDao
import ac.cord.auxiliary.compose.database.dao.NostrEventRelayDao
import ac.cord.auxiliary.compose.database.dao.NostrNip17Dao
import ac.cord.auxiliary.compose.database.dao.ParticipantDao
import ac.cord.auxiliary.compose.database.dao.PostDao
import ac.cord.auxiliary.compose.database.dao.ProfileDao
import ac.cord.auxiliary.compose.database.dao.QuotedRelationDao
import ac.cord.auxiliary.compose.database.dao.ReactionDao
import ac.cord.auxiliary.compose.database.dao.RecentSearchDao
import ac.cord.auxiliary.compose.database.dao.RelayDao
import ac.cord.auxiliary.compose.database.dao.RepostedRelationDao
import ac.cord.auxiliary.compose.database.dao.SynchronizeNostrEventRequestDao
import ac.cord.auxiliary.compose.database.dao.SynchronizeNostrEventResultDao
import ac.cord.auxiliary.compose.database.dao.UnsignedNostrEventDao
import ac.cord.auxiliary.compose.database.dao.ZapDao
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventReceipt
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest
import ac.cord.auxiliary.compose.database.model.ChatMessage
import ac.cord.auxiliary.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation
import ac.cord.auxiliary.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
import ac.cord.auxiliary.compose.database.model.ChatMessageNostrEventRelation
import ac.cord.auxiliary.compose.database.model.ChatRoom
import ac.cord.auxiliary.compose.database.model.Connection
import ac.cord.auxiliary.compose.database.model.GiftWrapMessage
import ac.cord.auxiliary.compose.database.model.GiftWrapSeal
import ac.cord.auxiliary.compose.database.model.GiftWrapPayload
import ac.cord.auxiliary.compose.database.model.InReplyToRelation
import ac.cord.auxiliary.compose.database.model.Mention
import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest
import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeResult
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.database.model.NostrEventRelay
import ac.cord.auxiliary.compose.database.model.Participant
import ac.cord.auxiliary.compose.database.model.Post
import ac.cord.auxiliary.compose.database.model.Profile
import ac.cord.auxiliary.compose.database.model.QuotedRelation
import ac.cord.auxiliary.compose.database.model.Reaction
import ac.cord.auxiliary.compose.database.model.RecentSearch
import ac.cord.auxiliary.compose.database.model.Relay
import ac.cord.auxiliary.compose.database.model.RepostedRelation
import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest
import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventResult
import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent
import ac.cord.auxiliary.compose.database.model.Zap
import androidx.room3.Database
import androidx.room3.RoomDatabase
import androidx.room3.TypeConverters
import androidx.room3.immediateTransaction
import androidx.room3.useWriterConnection
import kotlin.time.Instant
val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
@Database(
entities = [
BroadcastNostrEventReceipt::class,
BroadcastNostrEventRequest::class,
Connection::class,
ChatMessage::class,
ChatMessageBroadcastNostrEventRequestRelation::class,
ChatMessageBroadcastNostrEventReceiptRelation::class,
ChatMessageNostrEventRelation::class,
ChatRoom::class,
InReplyToRelation::class,
GiftWrapMessage::class,
GiftWrapSeal::class,
GiftWrapPayload::class,
Mention::class,
NegentropySynchronizeRequest::class,
NegentropySynchronizeResult::class,
NostrEvent::class,
NostrEventRelay::class,
Participant::class,
Post::class,
Profile::class,
QuotedRelation::class,
Reaction::class,
RecentSearch::class,
Relay::class,
RepostedRelation::class,
SynchronizeNostrEventRequest::class,
SynchronizeNostrEventResult::class,
UnsignedNostrEvent::class,
Zap::class
],
version = 1
)
@TypeConverters(AuxConverters::class)
abstract class AuxDatabase: RoomDatabase() {
abstract fun broadcastNostrEventReceiptDao(): BroadcastNostrEventReceiptDao
abstract fun broadcastNostrEventRequestDao(): BroadcastNostrEventRequestDao
abstract fun chatMessageDao(): ChatMessageDao
abstract fun chatMessageBroadcastNostrEventRequestRelationDao(): ChatMessageBroadcastNostrEventRequestRelationDao
abstract fun chatMessageBroadcastNostrEventReceiptRelationDao(): ChatMessageBroadcastNostrEventReceiptRelationDao
abstract fun chatMessageNostrEventRelationDao(): ChatMessageNostrEventRelationDao
abstract fun chatRoomDao(): ChatRoomDao
abstract fun connectionDao(): ConnectionDao
abstract fun giftWrapMessageDao(): GiftWrapMessageDao
abstract fun giftWrapSealDao(): GiftWrapSealDao
abstract fun giftWrapPayloadDao(): GiftWrapPayloadDao
abstract fun inReplyToRelationDao(): InReplyToRelationDao
abstract fun mentionDao(): MentionDao
abstract fun negentropySynchronizeRequestDao(): NegentropySynchronizeRequestDao
abstract fun negentropySynchronizeResultDao(): NegentropySynchronizeResultDao
abstract fun nostrDao(): NostrDao
abstract fun nostrEventDao(): NostrEventDao
abstract fun nostrEventRelayDao(): NostrEventRelayDao
abstract fun nostrNip17Dao(): NostrNip17Dao
abstract fun participantDao(): ParticipantDao
abstract fun postDao(): PostDao
abstract fun profileDao(): ProfileDao
abstract fun quotedRelationDao(): QuotedRelationDao
abstract fun reactionDao(): ReactionDao
abstract fun recentSearchDao(): RecentSearchDao
abstract fun relayDao(): RelayDao
abstract fun repostedRelationDao(): RepostedRelationDao
abstract fun synchronizeNostrEventRequestDao(): SynchronizeNostrEventRequestDao
abstract fun synchronizeNostrEventResultDao(): SynchronizeNostrEventResultDao
abstract fun unsignedNostrEventDao(): UnsignedNostrEventDao
abstract fun zapDao(): ZapDao
suspend fun wipeData() {
useWriterConnection { transactor ->
transactor.immediateTransaction {
// TODO: Actually delete the data...
}
}
}
}

View File

@@ -1,9 +0,0 @@
package ac.cord.auxiliary.compose.database
import androidx.room3.RoomDatabaseConstructor
@Suppress("KotlinNoActualForExpect", "NO_ACTUAL_FOR_EXPECT")
expect object AuxDatabaseConstructor: RoomDatabaseConstructor<AuxDatabase> {
override fun initialize(): AuxDatabase
}

View File

@@ -1,23 +0,0 @@
package ac.cord.auxiliary.compose.database.builder
import ac.cord.auxiliary.compose.PlatformContext
import ac.cord.auxiliary.compose.database.AuxDatabase
import androidx.room3.RoomDatabase
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
expect object PlatformDatabaseBuilder {
fun getDatabaseBuilder(platformContext: PlatformContext): RoomDatabase.Builder<AuxDatabase>
fun getInMemoryDatabaseBuilder(platform: PlatformContext): RoomDatabase.Builder<AuxDatabase>
}
fun getRoomDatabase(
builder: RoomDatabase.Builder<AuxDatabase>
): AuxDatabase {
return builder
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}

View File

@@ -1,22 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventReceipt
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface BroadcastNostrEventReceiptDao {
@Query("SELECT * FROM BroadcastNostrEventReceipt")
fun getAllBroadcastNostrEventReceipts(): List<BroadcastNostrEventReceipt>
@Query("SELECT * FROM BroadcastNostrEventReceipt WHERE id = :id")
fun getBroadcastNostrEventReceiptById(id: Long): Flow<BroadcastNostrEventReceipt?>
@Query("SELECT * FROM BroadcastNostrEventReceipt WHERE nostrEventId = :nostrEventId")
fun getBroadcastNostrEventReceiptByNostrEventId(nostrEventId: String): Flow<BroadcastNostrEventReceipt?>
@Upsert
suspend fun upsert(broadcastNostrEventReceipt: BroadcastNostrEventReceipt): Long
}

View File

@@ -1,20 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
import ac.cord.auxiliary.compose.database.model.NostrEvent
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Transaction
import androidx.room3.Upsert
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Kind
import kotlin.time.Instant
@Dao
interface ChatMessageBroadcastNostrEventRequestRelationDao {
@Query("SELECT * FROM ChatMessageBroadcastNostrEventRequestRelation WHERE broadcastNostrEventRequestId = :broadcastNostrEventRequestId")
suspend fun getChatMessageBroadcastNostrEventRequestRelationByBroadcastRequest(broadcastNostrEventRequestId: Long): ChatMessageBroadcastNostrEventRequestRelation?
@Upsert
suspend fun upsert(chatMessageBroadcastNostrEventRequestRelation: ChatMessageBroadcastNostrEventRequestRelation)
}

View File

@@ -1,12 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.ChatMessageNostrEventRelation
import androidx.room3.Dao
import androidx.room3.Upsert
@Dao
interface ChatMessageNostrEventRelationDao {
@Upsert
suspend fun upsert(chatMessageNostrEventRelation: ChatMessageNostrEventRelation)
}

View File

@@ -1,18 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.GiftWrapMessage
import ac.cord.auxiliary.compose.database.model.Relay
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface GiftWrapMessageDao {
@Query("SELECT * FROM GiftWrapMessage")
fun getAllGiftWrapMessages(): List<GiftWrapMessage>
@Upsert
suspend fun upsert(giftWrapMessage: GiftWrapMessage)
}

View File

@@ -1,18 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.GiftWrapSeal
import ac.cord.auxiliary.compose.database.model.Relay
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface GiftWrapSealDao {
@Query("SELECT * FROM GiftWrapSeal")
fun getAllGiftWrapSeals(): List<GiftWrapSeal>
@Upsert
suspend fun upsert(giftWrapSeal: GiftWrapSeal)
}

View File

@@ -1,22 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.Mention
import ac.cord.auxiliary.compose.database.model.Post
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalPost
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
import androidx.room3.Query
import androidx.room3.Upsert
@Dao
interface MentionDao {
@Insert(
onConflict = IGNORE
)
suspend fun insert(mention: Mention)
@Upsert
suspend fun upsert(mention: Mention)
}

View File

@@ -1,16 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeResult
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
import kotlin.time.Clock
import kotlin.time.Instant
@Dao
interface NegentropySynchronizeResultDao {
@Upsert
fun upsert(negentropySynchronizeResult: NegentropySynchronizeResult)
}

View File

@@ -1,16 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.NostrEventRelay
import ac.cord.auxiliary.compose.database.model.Relay
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface NostrEventRelayDao {
@Upsert
suspend fun upsert(nostrEventRelay: NostrEventRelay)
}

View File

@@ -1,24 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.ChatRoom
import ac.cord.auxiliary.compose.database.model.Participant
import ac.cord.auxiliary.compose.database.model.Relay
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import fr.acinq.bitcoin.PublicKey
import kotlinx.coroutines.flow.Flow
@Dao
interface ParticipantDao {
@Query("SELECT * FROM Participant WHERE participantPublicKey = :participantPublicKey")
fun findParticipantByPublicKey(participantPublicKey: String): Participant?
@Query("SELECT * FROM Participant WHERE chatRoomId = :chatRoomId")
fun findParticipantsByChatRoomId(chatRoomId: String): List<Participant>
@Upsert
suspend fun upsert(participants: List<Participant>)
}

View File

@@ -1,23 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.Post
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalPost
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.Query
import androidx.room3.Upsert
@Dao
interface PostDao {
@Query("SELECT * FROM Post")
fun getAllPosts(): List<LocalPost>
@Query("SELECT * FROM Post WHERE id = :id")
fun getPostById(id: String): Post?
@Insert
suspend fun insert(post: Post)
@Upsert
suspend fun upsert(post: Post)
}

View File

@@ -1,19 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.Reaction
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface ReactionDao {
@Query("SELECT * FROM Reaction")
fun getAllReactions(): List<Reaction>
@Query("SELECT * FROM Reaction WHERE id = :id")
fun getReactionById(id: String): Flow<Reaction?>
@Upsert
suspend fun upsert(reaction: Reaction)
}

View File

@@ -1,23 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.RecentSearch
import androidx.room3.Dao
import androidx.room3.Delete
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface RecentSearchDao {
@Query("SELECT * FROM RecentSearch WHERE `query` = :query")
fun observeRecentSearchByQuery(query: String): Flow<RecentSearch?>
@Query("SELECT * FROM RecentSearch ORDER BY createdAt DESC LIMIT :limit ")
fun getAllRecentSearches(limit: Int = 21): List<RecentSearch>
@Delete
fun deleteRecentSearch(recentSearch: RecentSearch)
@Upsert
suspend fun upsert(recentSearch: RecentSearch)
}

View File

@@ -1,20 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.Relay
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface RelayDao {
@Query("SELECT * FROM Relay")
fun getAllPosts(): List<Relay>
@Query("SELECT * FROM Relay WHERE publicKey = :publicKey")
fun observePublicKeyRelays(publicKey: String): Flow<List<Relay>>
@Upsert
suspend fun upsert(relay: Relay)
}

View File

@@ -1,21 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.RepostedRelation
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
@Dao
interface RepostedRelationDao {
@Query("SELECT * FROM RepostedRelation")
fun getAllReposts(): List<RepostedRelation>
@Query("SELECT * FROM RepostedRelation WHERE repostingNostrEventId = :repostingNostrEventId")
fun getRepostByRepostingId(repostingNostrEventId: String): RepostedRelation?
@Query("SELECT * FROM RepostedRelation WHERE repostedNostrEventId = :repostedNostrEventId")
fun getRepostByRepostedId(repostedNostrEventId: String): RepostedRelation?
@Upsert
suspend fun upsert(repostedRelation: RepostedRelation)
}

View File

@@ -1,11 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventResult
import androidx.room3.Dao
import androidx.room3.Upsert
@Dao
interface SynchronizeNostrEventResultDao {
@Upsert
fun upsert(synchronizeNostrEventResult: SynchronizeNostrEventResult)
}

View File

@@ -1,35 +0,0 @@
package ac.cord.auxiliary.compose.database.dao
import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalAccount
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.Query
import androidx.room3.Transaction
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface UnsignedNostrEventDao {
@Query("SELECT * FROM UnsignedNostrEvent")
fun getAUnsignedNostrEvents(): List<UnsignedNostrEvent>
@Query("SELECT * FROM UnsignedNostrEvent") // TODO: where nostrEvent.unsignedNostrEventId is 0
fun getAllUnprocessedUnsignedNostrEvents(): List<UnsignedNostrEvent>
@Query("SELECT * FROM UnsignedNostrEvent WHERE id = :id")
fun getUnsignedNostrEventById(id: Long): Flow<UnsignedNostrEvent?>
@Upsert
suspend fun upsert(unsignedNostrEvent: UnsignedNostrEvent): Long
@Insert
suspend fun insert(unsignedNostrEvents: List<UnsignedNostrEvent>)
@Transaction
@Query("SELECT * FROM UnsignedNostrEvent WHERE kind = 0 AND pubKey = :publicKey")
fun observeProfile(publicKey: String): Flow<LocalAccount?>
@Query("SELECT * FROM UnsignedNostrEvent WHERE pubKey = :publicKey AND signedAt IS NULL")
fun observeUnsignedNostrEvents(publicKey: String): Flow<UnsignedNostrEvent?>
}

View File

@@ -1,45 +0,0 @@
package ac.cord.auxiliary.compose.database.model.intermdiate
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventReceipt
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.database.model.Profile
import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest
import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent
import androidx.room3.Embedded
import androidx.room3.Relation
data class LocalAccount(
@Embedded val unsignedNostrEvent: UnsignedNostrEvent?,
@Relation(
parentColumns = ["id"],
entityColumns = ["unsignedNostrEventId"]
)
val nostrEvent: NostrEvent?,
@Relation(
parentColumns = ["pubKey"],
entityColumns = ["publicKey"]
)
val profile: Profile?,
@Relation(
parentColumns = ["id"],
entityColumns = ["unsignedNostrEventId"]
)
val broadcastNostrEventRequest: BroadcastNostrEventRequest?,
@Relation(
parentColumns = ["id"],
entityColumns = ["unsignedNostrEventId"]
)
val broadcastNostrEventReceipt: BroadcastNostrEventReceipt?,
@Relation(
parentColumns = ["id"],
entityColumns = ["unsignedNostrEventId"]
)
val synchronizeNostrEventRequests: List<SynchronizeNostrEventRequest>,
)

View File

@@ -1,16 +0,0 @@
package ac.cord.auxiliary.compose.database.model.intermdiate
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest
import ac.cord.auxiliary.compose.database.model.NostrEvent
import androidx.room3.Embedded
import androidx.room3.Relation
data class LocalBroadcastNostrEventRequest(
@Embedded val broadcastNostrEventRequest: BroadcastNostrEventRequest,
@Relation(
parentColumns = ["nostrEventId"],
entityColumns = ["id"]
)
val nostrEvent: NostrEvent,
)

View File

@@ -1,40 +0,0 @@
package ac.cord.auxiliary.compose.database.model.intermdiate
import ac.cord.auxiliary.compose.database.model.ChatMessage
import ac.cord.auxiliary.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation
import ac.cord.auxiliary.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
import ac.cord.auxiliary.compose.database.model.ChatMessageNostrEventRelation
import ac.cord.auxiliary.compose.database.model.GiftWrapPayload
import ac.cord.auxiliary.compose.database.model.Profile
import androidx.room3.Embedded
import androidx.room3.Relation
data class LocalChatMessage(
@Embedded val chatMessage: ChatMessage,
@Relation(
parentColumns = ["senderPublicKey"],
entityColumns = ["publicKey"]
)
val profile: Profile?,
@Relation(
parentColumns = ["id"],
entityColumns = ["chatMessageId"]
)
val chatMessageBroadcastNostrEventReceiptRelation: ChatMessageBroadcastNostrEventReceiptRelation? = null,
@Relation(
parentColumns = ["id"],
entityColumns = ["chatMessageId"]
)
val chatMessageBroadcastNostrEventRequestRelation: ChatMessageBroadcastNostrEventRequestRelation? = null,
@Relation(
parentColumns = ["id"],
entityColumns = ["chatMessageId"]
)
val chatMessageNostrEventRelation: ChatMessageNostrEventRelation? = null,
)

View File

@@ -1,16 +0,0 @@
package ac.cord.auxiliary.compose.database.model.intermdiate
import ac.cord.auxiliary.compose.database.model.Mention
import ac.cord.auxiliary.compose.database.model.Profile
import androidx.room3.Embedded
import androidx.room3.Relation
data class LocalMention(
@Embedded val mention: Mention,
@Relation(
parentColumns = ["mentionedPublicKey"],
entityColumns = ["publicKey"]
)
val profile: Profile?,
)

View File

@@ -1,16 +0,0 @@
package ac.cord.auxiliary.compose.database.model.intermdiate
import ac.cord.auxiliary.compose.database.model.Participant
import ac.cord.auxiliary.compose.database.model.Profile
import androidx.room3.Embedded
import androidx.room3.Relation
data class LocalParticipant(
@Embedded val participant: Participant,
@Relation(
parentColumns = ["participantPublicKey"],
entityColumns = ["publicKey"]
)
val profile: Profile?,
)

View File

@@ -1,16 +0,0 @@
package ac.cord.auxiliary.compose.database.model.intermdiate
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.database.model.Profile
import androidx.room3.Embedded
import androidx.room3.Relation
data class LocalProfile(
@Embedded val nostrEvent: NostrEvent,
@Relation(
parentColumns = ["pubKey"],
entityColumns = ["publicKey"]
)
val profile: Profile,
)

View File

@@ -1,4 +0,0 @@
package ac.cord.auxiliary.compose.database.model.typealiases
typealias HexArray = Array<String>

View File

@@ -1,3 +0,0 @@
package ac.cord.auxiliary.compose.database.model.typealiases
typealias KindArray = Array<Int>

View File

@@ -1,5 +0,0 @@
package ac.cord.auxiliary.compose.database.model.typealiases
import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter
typealias SynchronizationFilterArray = Array<SynchronizationFilter>

View File

@@ -1,10 +0,0 @@
package ac.cord.auxiliary.compose.database.repository
import ac.cord.auxiliary.compose.database.AuxDatabase
import ac.cord.auxiliary.compose.repository.PostRepository
class DatabasePostRepository(
private val database: AuxDatabase
): PostRepository {
}

View File

@@ -1,16 +0,0 @@
package ac.cord.auxiliary.compose.database.repository
import ac.cord.auxiliary.compose.database.AuxDatabase
import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest
import ac.cord.auxiliary.compose.repository.SearchRepository
class DatabaseSearchRepository(
private val database: AuxDatabase
): SearchRepository {
override suspend fun submitSearch(synchronizeNostrEventRequests: List<SynchronizeNostrEventRequest>) {
database.synchronizeNostrEventRequestDao().insert(
synchronizeNostrEventRequests = synchronizeNostrEventRequests
)
}
}

View File

@@ -1,3 +0,0 @@
package ac.cord.auxiliary.compose.exceptions
class InvalidNostrPrivateKeyException: RuntimeException()

View File

@@ -1,30 +0,0 @@
package ac.cord.auxiliary.compose.managers
import ac.cord.auxiliary.compose.AuxGlobal
import ac.cord.auxiliary.compose.database.builder.PlatformDatabaseBuilder
import ac.cord.auxiliary.compose.database.builder.getRoomDatabase
class AuxDatabaseManager(
auxGlobal: AuxGlobal
) {
val auxDatabase by lazy {
getRoomDatabase(
PlatformDatabaseBuilder.getDatabaseBuilder(
auxGlobal.platformContext
)
)
}
/**
* Dynamic data like user feeds and the likes will be in a in-memory db.
*
* We may also decide to just use the normal DB and manage what's saved better...
*/
val inMemoryAuxDatabase by lazy {
getRoomDatabase(
PlatformDatabaseBuilder.getInMemoryDatabaseBuilder(
auxGlobal.platformContext
)
)
}
}

View File

@@ -1,43 +0,0 @@
package ac.cord.auxiliary.compose.repository
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.network.relays.broadcast.BroadcastEventResponse
/**
* As seen in Primal
*
* https://github.com/PrimalHQ/primal-android-app/blob/a7e32e2203555d27e60a749238175cd1ee0fa2af/domain/primal/src/commonMain/kotlin/net/primal/domain/global/CachingImportRepository.kt
*
*/
interface CachingImportRepository {
suspend fun cacheNostrEvents(vararg events: NostrEvent)
suspend fun cacheNostrEvents(events: List<NostrEvent>)
suspend fun importEvents(events: List<NostrEvent>): Boolean
suspend fun broadcastEvents(events: List<NostrEvent>, relays: List<String>): Result<List<BroadcastEventResponse>>
companion object {
val NO_OP_CACHING_IMPORT_REPOSITORY = object : CachingImportRepository {
override suspend fun cacheNostrEvents(vararg events: NostrEvent) {
}
override suspend fun cacheNostrEvents(events: List<NostrEvent>) {
}
override suspend fun importEvents(events: List<NostrEvent>): Boolean {
return true
}
override suspend fun broadcastEvents(
events: List<NostrEvent>,
relays: List<String>
): Result<List<BroadcastEventResponse>> {
return Result.success(
emptyList()
)
}
}
}
}

View File

@@ -1,4 +0,0 @@
package ac.cord.auxiliary.compose.repository
interface PostRepository {
}

View File

@@ -1,6 +0,0 @@
package ac.cord.auxiliary.compose.ui.composable.navigation.routes
import kotlinx.serialization.Serializable
@Serializable
object BlankRoute: Route()

View File

@@ -1,6 +0,0 @@
package ac.cord.auxiliary.compose.ui.composable.navigation.routes
import kotlinx.serialization.Serializable
@Serializable
object LandingRoute: Route()

View File

@@ -1,3 +0,0 @@
package ac.cord.auxiliary.compose.ui.composable.navigation.routes
abstract class Route

View File

@@ -1,6 +0,0 @@
package ac.cord.auxiliary.compose.ui.composable.navigation.routes
import kotlinx.serialization.Serializable
@Serializable
object SignInRoute: Route()

View File

@@ -1,6 +0,0 @@
package ac.cord.auxiliary.compose.ui.composable.navigation.routes
import kotlinx.serialization.Serializable
@Serializable
data object SovereignWalletStartupRoute

View File

@@ -1,13 +0,0 @@
package ac.cord.auxiliary.compose.ui.view.state
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalNostrEvent
sealed interface FeedListUIState {
data class Loaded(
val localNostrEvents: List<LocalNostrEvent>
): FeedListUIState
data object Error: FeedListUIState
data object Loading: FeedListUIState
}

View File

@@ -1,15 +0,0 @@
package ac.cord.auxiliary.compose.ui.view.state
import ac.cord.auxiliary.compose.database.model.Connection
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing
sealed interface HomeScreenUIState {
data class Loaded(
val profileWithFollowing: LocalProfileWithFollowing
): HomeScreenUIState
data object Error: HomeScreenUIState
data object Loading: HomeScreenUIState
}

View File

@@ -1,27 +0,0 @@
package ac.cord.auxiliary.compose.ui.view.state
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalNostrEvent
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfile
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing
import com.vitorpamplona.quartz.nip01Core.core.HexKey
abstract class WriteNewNoteUIState {
data object Loading: WriteNewNoteUIState()
data object Error: WriteNewNoteUIState()
data class InputPrompt(
val localProfile: LocalProfileWithFollowing,
val inReplyToNostrEvent: LocalNostrEvent? = null,
val quotedNostrEvent: LocalNostrEvent? = null,
val replayToId: HexKey? = null,
): WriteNewNoteUIState()
data class ConfirmNote(
val localProfile: LocalProfileWithFollowing,
val inReplyToNostrEvent: LocalNostrEvent? = null,
val quotedNostrEvent: LocalNostrEvent? = null,
val text: String,
) : WriteNewNoteUIState()
}

View File

@@ -1,7 +0,0 @@
package ac.cord.auxiliary.compose.ui.view.state.form
import androidx.compose.foundation.text.input.TextFieldState
data class WriteNewNoteFormState(
val textField: TextField = TextField(),
)

View File

@@ -1,4 +1,4 @@
package ac.cord.auxiliary.compose
package at.torch.compose
expect object AppVersion {
val versionName: String

View File

@@ -0,0 +1,7 @@
package at.torch.compose
interface Platform {
val name: String
}
expect fun getPlatform(): Platform

View File

@@ -0,0 +1,3 @@
package at.torch.compose
expect class PlatformContext

View File

@@ -1,7 +1,7 @@
package ac.cord.auxiliary.compose
package at.torch.compose
import ac.cord.auxiliary.compose.ui.composable.navigation.AuxNavHost
import ac.cord.auxiliary.compose.ui.theme.AuxTheme
import at.torch.compose.ui.composable.navigation.TorchNavHost
import at.torch.compose.ui.theme.TorchTheme
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
@@ -10,17 +10,17 @@ import androidx.navigation.NavHostController
import fr.acinq.phoenix.PhoenixGlobal
@Composable
fun AuxApp(
fun TorchApp(
navController: NavHostController,
auxGlobal: AuxGlobal,
torchGlobal: TorchGlobal,
phoenixGlobal: PhoenixGlobal
) {
AuxTheme {
TorchTheme {
Surface(
modifier = Modifier.fillMaxSize()
) {
AuxNavHost(
auxGlobal = auxGlobal,
TorchNavHost(
torchGlobal = torchGlobal,
phoenixGlobal = phoenixGlobal,
navController = navController
)

View File

@@ -0,0 +1,7 @@
package at.torch.compose
class TorchGlobal(
val platformContext: PlatformContext
) {
}

View File

@@ -1,7 +1,6 @@
package ac.cord.auxiliary.compose.cryptography
package at.torch.compose.cryptography
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent
import at.torch.compose.database.model.NostrEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerSync
@@ -9,12 +8,12 @@ import com.vitorpamplona.quartz.nip19Bech32.bech32.Bech32
import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
import kotlin.time.Instant
fun UnsignedNostrEvent.signOrThrow(nsec: String): NostrEvent {
fun at.torch.compose.database.model.UnsignedNostrEvent.signOrThrow(nsec: String): at.torch.compose.database.model.NostrEvent {
val hexPrivateKey = Bech32.decodeBytes(nsec).second
return this.signOrThrow(hexPrivateKey)
}
fun UnsignedNostrEvent.signOrThrow(hexPrivateKey: ByteArray): NostrEvent {
fun at.torch.compose.database.model.UnsignedNostrEvent.signOrThrow(hexPrivateKey: ByteArray): at.torch.compose.database.model.NostrEvent {
val tempSigner = NostrSignerSync(
KeyPair(
privKey = hexPrivateKey
@@ -27,7 +26,7 @@ fun UnsignedNostrEvent.signOrThrow(hexPrivateKey: ByteArray): NostrEvent {
tags = this.tags,
content = this.privateTags?.let { PrivateTagsInContent.encryptNip44(it, tempSigner) } ?: this.content
)
return NostrEvent(
return _root_ide_package_.ac.cord.compose.database.model.NostrEvent(
id = event.id,
pubKey = event.pubKey,
kind = event.kind,

View File

@@ -0,0 +1,115 @@
package at.torch.compose.database
import androidx.room3.Database
import androidx.room3.RoomDatabase
import androidx.room3.TypeConverters
import androidx.room3.immediateTransaction
import androidx.room3.useWriterConnection
import kotlin.time.Instant
val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
@Database(
entities = [
_root_ide_package_.at.torch.compose.database.model.BroadcastNostrEventReceipt::class,
_root_ide_package_.at.torch.compose.database.model.BroadcastNostrEventRequest::class,
_root_ide_package_.at.torch.compose.database.model.Connection::class,
_root_ide_package_.at.torch.compose.database.model.ChatMessage::class,
_root_ide_package_.at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation::class,
_root_ide_package_.at.torch.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation::class,
_root_ide_package_.at.torch.compose.database.model.ChatMessageNostrEventRelation::class,
_root_ide_package_.at.torch.compose.database.model.ChatRoom::class,
_root_ide_package_.at.torch.compose.database.model.InReplyToRelation::class,
_root_ide_package_.at.torch.compose.database.model.GiftWrapMessage::class,
_root_ide_package_.at.torch.compose.database.model.GiftWrapSeal::class,
_root_ide_package_.at.torch.compose.database.model.GiftWrapPayload::class,
_root_ide_package_.at.torch.compose.database.model.Mention::class,
_root_ide_package_.at.torch.compose.database.model.NegentropySynchronizeRequest::class,
_root_ide_package_.at.torch.compose.database.model.NegentropySynchronizeResult::class,
_root_ide_package_.at.torch.compose.database.model.NostrEvent::class,
_root_ide_package_.at.torch.compose.database.model.NostrEventRelay::class,
_root_ide_package_.at.torch.compose.database.model.Participant::class,
_root_ide_package_.at.torch.compose.database.model.Post::class,
_root_ide_package_.at.torch.compose.database.model.Profile::class,
_root_ide_package_.at.torch.compose.database.model.QuotedRelation::class,
_root_ide_package_.at.torch.compose.database.model.Reaction::class,
_root_ide_package_.at.torch.compose.database.model.RecentSearch::class,
_root_ide_package_.at.torch.compose.database.model.Relay::class,
_root_ide_package_.at.torch.compose.database.model.RepostedRelation::class,
_root_ide_package_.at.torch.compose.database.model.SynchronizeNostrEventRequest::class,
_root_ide_package_.at.torch.compose.database.model.SynchronizeNostrEventResult::class,
_root_ide_package_.at.torch.compose.database.model.UnsignedNostrEvent::class,
_root_ide_package_.at.torch.compose.database.model.Zap::class
],
version = 1
)
@TypeConverters(_root_ide_package_.at.torch.compose.database.converters.AuxConverters::class)
abstract class TorchDatabase: RoomDatabase() {
abstract fun broadcastNostrEventReceiptDao(): at.torch.compose.database.dao.BroadcastNostrEventReceiptDao
abstract fun broadcastNostrEventRequestDao(): at.torch.compose.database.dao.BroadcastNostrEventRequestDao
abstract fun chatMessageDao(): at.torch.compose.database.dao.ChatMessageDao
abstract fun chatMessageBroadcastNostrEventRequestRelationDao(): at.torch.compose.database.dao.ChatMessageBroadcastNostrEventRequestRelationDao
abstract fun chatMessageBroadcastNostrEventReceiptRelationDao(): at.torch.compose.database.dao.ChatMessageBroadcastNostrEventReceiptRelationDao
abstract fun chatMessageNostrEventRelationDao(): at.torch.compose.database.dao.ChatMessageNostrEventRelationDao
abstract fun chatRoomDao(): at.torch.compose.database.dao.ChatRoomDao
abstract fun connectionDao(): at.torch.compose.database.dao.ConnectionDao
abstract fun giftWrapMessageDao(): at.torch.compose.database.dao.GiftWrapMessageDao
abstract fun giftWrapSealDao(): at.torch.compose.database.dao.GiftWrapSealDao
abstract fun giftWrapPayloadDao(): at.torch.compose.database.dao.GiftWrapPayloadDao
abstract fun inReplyToRelationDao(): at.torch.compose.database.dao.InReplyToRelationDao
abstract fun mentionDao(): at.torch.compose.database.dao.MentionDao
abstract fun negentropySynchronizeRequestDao(): at.torch.compose.database.dao.NegentropySynchronizeRequestDao
abstract fun negentropySynchronizeResultDao(): at.torch.compose.database.dao.NegentropySynchronizeResultDao
abstract fun nostrDao(): at.torch.compose.database.dao.NostrDao
abstract fun nostrEventDao(): at.torch.compose.database.dao.NostrEventDao
abstract fun nostrEventRelayDao(): at.torch.compose.database.dao.NostrEventRelayDao
abstract fun nostrNip17Dao(): at.torch.compose.database.dao.NostrNip17Dao
abstract fun participantDao(): at.torch.compose.database.dao.ParticipantDao
abstract fun postDao(): at.torch.compose.database.dao.PostDao
abstract fun profileDao(): at.torch.compose.database.dao.ProfileDao
abstract fun quotedRelationDao(): at.torch.compose.database.dao.QuotedRelationDao
abstract fun reactionDao(): at.torch.compose.database.dao.ReactionDao
abstract fun recentSearchDao(): at.torch.compose.database.dao.RecentSearchDao
abstract fun relayDao(): at.torch.compose.database.dao.RelayDao
abstract fun repostedRelationDao(): at.torch.compose.database.dao.RepostedRelationDao
abstract fun synchronizeNostrEventRequestDao(): at.torch.compose.database.dao.SynchronizeNostrEventRequestDao
abstract fun synchronizeNostrEventResultDao(): at.torch.compose.database.dao.SynchronizeNostrEventResultDao
abstract fun unsignedNostrEventDao(): at.torch.compose.database.dao.UnsignedNostrEventDao
abstract fun zapDao(): at.torch.compose.database.dao.ZapDao
suspend fun wipeData() {
useWriterConnection { transactor ->
transactor.immediateTransaction {
// TODO: Actually delete the data...
}
}
}
}

View File

@@ -0,0 +1,9 @@
package at.torch.compose.database
import androidx.room3.RoomDatabaseConstructor
@Suppress("KotlinNoActualForExpect", "NO_ACTUAL_FOR_EXPECT")
expect object TorchDatabaseConstructor: RoomDatabaseConstructor<TorchDatabase> {
override fun initialize(): TorchDatabase
}

View File

@@ -0,0 +1,21 @@
package at.torch.compose.database.builder
import androidx.room3.RoomDatabase
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
expect object PlatformDatabaseBuilder {
fun getDatabaseBuilder(platformContext: at.torch.compose.PlatformContext): RoomDatabase.Builder<at.torch.compose.database.TorchDatabase>
fun getInMemoryDatabaseBuilder(platform: at.torch.compose.PlatformContext): RoomDatabase.Builder<at.torch.compose.database.TorchDatabase>
}
fun getRoomDatabase(
builder: RoomDatabase.Builder<at.torch.compose.database.TorchDatabase>
): at.torch.compose.database.TorchDatabase {
return builder
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}

View File

@@ -1,12 +1,7 @@
package ac.cord.auxiliary.compose.database.converters
package at.torch.compose.database.converters
import ac.cord.auxiliary.compose.database.model.typealiases.SynchronizationFilterArray
import ac.cord.auxiliary.compose.database.model.typealiases.HexArray
import ac.cord.auxiliary.compose.database.model.typealiases.KindArray
import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter
import androidx.room3.TypeConverter
import co.touchlab.kermit.Logger
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import kotlinx.serialization.json.Json
import kotlin.time.Instant
@@ -77,7 +72,7 @@ class AuxConverters {
}
@TypeConverter
fun fromFilterArray(value: Array<SynchronizationFilter>?): String? = try {
fun fromFilterArray(value: Array<at.torch.compose.database.model.types.SynchronizationFilter>?): String? = try {
return value?.let {
Json.encodeToString(value)
}
@@ -87,9 +82,9 @@ class AuxConverters {
}
@TypeConverter
fun toFilterArray(value: String?): Array<SynchronizationFilter>? = try {
fun toFilterArray(value: String?): Array<at.torch.compose.database.model.types.SynchronizationFilter>? = try {
return value?.let {
Json.decodeFromString<Array<SynchronizationFilter>>(it)
Json.decodeFromString<Array<at.torch.compose.database.model.types.SynchronizationFilter>>(it)
}
} catch (e: Throwable) {
logger.e("Failed to convert to FilterArray $value", e)
@@ -97,7 +92,7 @@ class AuxConverters {
}
@TypeConverter
fun fromSynchronizationFilter(value: SynchronizationFilter?): String? = try {
fun fromSynchronizationFilter(value: at.torch.compose.database.model.types.SynchronizationFilter?): String? = try {
return value?.let {
Json.encodeToString(value)
}
@@ -107,9 +102,9 @@ class AuxConverters {
}
@TypeConverter
fun toSynchronizationFilter(value: String?): SynchronizationFilter? = try {
fun toSynchronizationFilter(value: String?): at.torch.compose.database.model.types.SynchronizationFilter? = try {
return value?.let {
Json.decodeFromString<SynchronizationFilter>(it)
Json.decodeFromString<at.torch.compose.database.model.types.SynchronizationFilter>(it)
}
} catch (e: Throwable) {
logger.e("Failed to convert to SynchronizationFilter $value", e)

View File

@@ -0,0 +1,21 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
interface BroadcastNostrEventReceiptDao {
@Query("SELECT * FROM BroadcastNostrEventReceipt")
fun getAllBroadcastNostrEventReceipts(): List<at.torch.compose.database.model.BroadcastNostrEventReceipt>
@Query("SELECT * FROM BroadcastNostrEventReceipt WHERE id = :id")
fun getBroadcastNostrEventReceiptById(id: Long): Flow<at.torch.compose.database.model.BroadcastNostrEventReceipt?>
@Query("SELECT * FROM BroadcastNostrEventReceipt WHERE nostrEventId = :nostrEventId")
fun getBroadcastNostrEventReceiptByNostrEventId(nostrEventId: String): Flow<at.torch.compose.database.model.BroadcastNostrEventReceipt?>
@Upsert
suspend fun upsert(broadcastNostrEventReceipt: at.torch.compose.database.model.BroadcastNostrEventReceipt): Long
}

View File

@@ -1,7 +1,5 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.Query
@@ -13,17 +11,17 @@ import kotlin.time.Instant
@Dao
interface BroadcastNostrEventRequestDao {
@Query("SELECT * FROM BroadcastNostrEventRequest")
fun getAllBroadcastNostrEventRequests(): List<BroadcastNostrEventRequest>
fun getAllBroadcastNostrEventRequests(): List<at.torch.compose.database.model.BroadcastNostrEventRequest>
@Query("SELECT * FROM BroadcastNostrEventRequest WHERE status = :status AND createdAt > :createdAt")
fun observeBroadcastNostrEventRequestsByStatus(status: String, createdAt: Instant = Clock.System.now()): Flow<List<LocalBroadcastNostrEventRequest>>
fun observeBroadcastNostrEventRequestsByStatus(status: String, createdAt: Instant = Clock.System.now()): Flow<List<at.torch.compose.database.model.intermdiate.LocalBroadcastNostrEventRequest>>
@Query("SELECT * FROM BroadcastNostrEventRequest WHERE nostrEventId = :nostrEventId")
fun observeBroadcastNostrEventRequestByNostrEventId(nostrEventId: String): Flow<BroadcastNostrEventRequest?>
fun observeBroadcastNostrEventRequestByNostrEventId(nostrEventId: String): Flow<at.torch.compose.database.model.BroadcastNostrEventRequest?>
@Upsert
suspend fun upsert(broadcastNostrEventRequest: BroadcastNostrEventRequest)
suspend fun upsert(broadcastNostrEventRequest: at.torch.compose.database.model.BroadcastNostrEventRequest)
@Insert
suspend fun insert(broadcastNostrEventRequests: List<BroadcastNostrEventRequest>): LongArray
suspend fun insert(broadcastNostrEventRequests: List<at.torch.compose.database.model.BroadcastNostrEventRequest>): LongArray
}

View File

@@ -1,11 +1,10 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation
import androidx.room3.Dao
import androidx.room3.Upsert
@Dao
interface ChatMessageBroadcastNostrEventReceiptRelationDao {
@Upsert
suspend fun upsert(chatMessageBroadcastNostrEventReceiptRelation: ChatMessageBroadcastNostrEventReceiptRelation)
suspend fun upsert(chatMessageBroadcastNostrEventReceiptRelation: at.torch.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation)
}

View File

@@ -0,0 +1,14 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
@Dao
interface ChatMessageBroadcastNostrEventRequestRelationDao {
@Query("SELECT * FROM ChatMessageBroadcastNostrEventRequestRelation WHERE broadcastNostrEventRequestId = :broadcastNostrEventRequestId")
suspend fun getChatMessageBroadcastNostrEventRequestRelationByBroadcastRequest(broadcastNostrEventRequestId: Long): at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation?
@Upsert
suspend fun upsert(chatMessageBroadcastNostrEventRequestRelation: at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation)
}

View File

@@ -1,8 +1,5 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.model.ChatMessage
import ac.cord.auxiliary.compose.database.model.Relay
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatMessage
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Transaction
@@ -13,15 +10,15 @@ import kotlinx.coroutines.flow.Flow
interface ChatMessageDao {
@Transaction
@Query("SELECT * FROM ChatMessage WHERE chatRoomId = :chatRoomId ORDER BY createdAt DESC")
fun observeChatMessagesByChatRoomId(chatRoomId: String): Flow<List<LocalChatMessage>>
fun observeChatMessagesByChatRoomId(chatRoomId: String): Flow<List<at.torch.compose.database.model.intermdiate.LocalChatMessage>>
@Transaction
@Query("SELECT * FROM ChatMessage WHERE chatRoomId = :chatRoomId ORDER BY createdAt DESC")
fun getChatMessagesByChatRoomId(chatRoomId: String): List<LocalChatMessage>
fun getChatMessagesByChatRoomId(chatRoomId: String): List<at.torch.compose.database.model.intermdiate.LocalChatMessage>
@Query("SELECT * FROM ChatMessage WHERE giftWrapPayloadId = :giftWrapPayloadId ORDER BY createdAt DESC")
fun getChatMessagesByGiftWrapPayloadId(giftWrapPayloadId: Long): ChatMessage?
fun getChatMessagesByGiftWrapPayloadId(giftWrapPayloadId: Long): at.torch.compose.database.model.ChatMessage?
@Upsert
suspend fun upsert(chatMessage: ChatMessage): Long
suspend fun upsert(chatMessage: at.torch.compose.database.model.ChatMessage): Long
}

View File

@@ -0,0 +1,11 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Upsert
@Dao
interface ChatMessageNostrEventRelationDao {
@Upsert
suspend fun upsert(chatMessageNostrEventRelation: at.torch.compose.database.model.ChatMessageNostrEventRelation)
}

View File

@@ -1,7 +1,5 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.model.ChatRoom
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Transaction
@@ -12,17 +10,17 @@ import kotlinx.coroutines.flow.Flow
interface ChatRoomDao {
@Transaction
@Query("SELECT * FROM ChatRoom WHERE id = :id")
fun findChatRoomById(id: String): LocalChatRoom?
fun findChatRoomById(id: String): at.torch.compose.database.model.intermdiate.LocalChatRoom?
@Transaction
@Query("SELECT * FROM ChatRoom WHERE userPublicKey = :userPublicKey")
fun getChatRoomListByUserPublicKey(userPublicKey: String): List<LocalChatRoom>
fun getChatRoomListByUserPublicKey(userPublicKey: String): List<at.torch.compose.database.model.intermdiate.LocalChatRoom>
@Transaction
@Query("SELECT * FROM ChatRoom WHERE userPublicKey = :userPublicKey")
fun observeChatRoomListByUserPublicKey(userPublicKey: String): Flow<List<LocalChatRoom>>
fun observeChatRoomListByUserPublicKey(userPublicKey: String): Flow<List<at.torch.compose.database.model.intermdiate.LocalChatRoom>>
@Upsert
suspend fun upsert(chatRoom: ChatRoom)
suspend fun upsert(chatRoom: at.torch.compose.database.model.ChatRoom)
}

View File

@@ -1,6 +1,5 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.model.Connection
import androidx.room3.Dao
import androidx.room3.Delete
import androidx.room3.Insert
@@ -12,19 +11,19 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface ConnectionDao {
@Query("SELECT * FROM Connection LIMIT :limit")
fun getAllConnections(limit: Int = 21): List<Connection>
fun getAllConnections(limit: Int = 21): List<at.torch.compose.database.model.Connection>
@Query("SELECT * FROM Connection WHERE (sourcePublicKey = :alicePublicKey AND destinationPublicKey = :bobPublicKey) OR (sourcePublicKey = :bobPublicKey AND destinationPublicKey = :alicePublicKey)")
fun observeRelation(alicePublicKey: HexKey, bobPublicKey: HexKey): Flow<List<Connection>>
fun observeRelation(alicePublicKey: HexKey, bobPublicKey: HexKey): Flow<List<at.torch.compose.database.model.Connection>>
@Insert
suspend fun insertPlaceholderProfile(connection: Connection)
suspend fun insertPlaceholderProfile(connection: at.torch.compose.database.model.Connection)
@Upsert
suspend fun upsert(connection: Connection)
suspend fun upsert(connection: at.torch.compose.database.model.Connection)
@Delete
suspend fun delete(connection: Connection)
suspend fun delete(connection: at.torch.compose.database.model.Connection)
}

View File

@@ -0,0 +1,15 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
@Dao
interface GiftWrapMessageDao {
@Query("SELECT * FROM GiftWrapMessage")
fun getAllGiftWrapMessages(): List<at.torch.compose.database.model.GiftWrapMessage>
@Upsert
suspend fun upsert(giftWrapMessage: at.torch.compose.database.model.GiftWrapMessage)
}

View File

@@ -1,6 +1,5 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.model.GiftWrapPayload
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
@@ -9,11 +8,11 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface GiftWrapPayloadDao {
@Query("SELECT * FROM GiftWrapPayload")
fun getAllGiftWrapUnsigned(): List<GiftWrapPayload>
fun getAllGiftWrapUnsigned(): List<at.torch.compose.database.model.GiftWrapPayload>
@Query("SELECT * FROM GiftWrapPayload WHERE publicKey = :publicKey AND giftWrapSealId IS NULL")
fun observeUnsealedGiftWrapPayloads(publicKey: String): Flow<GiftWrapPayload?>
fun observeUnsealedGiftWrapPayloads(publicKey: String): Flow<at.torch.compose.database.model.GiftWrapPayload?>
@Upsert
suspend fun upsert(giftWrapPayload: GiftWrapPayload): Long
suspend fun upsert(giftWrapPayload: at.torch.compose.database.model.GiftWrapPayload): Long
}

View File

@@ -0,0 +1,15 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
@Dao
interface GiftWrapSealDao {
@Query("SELECT * FROM GiftWrapSeal")
fun getAllGiftWrapSeals(): List<at.torch.compose.database.model.GiftWrapSeal>
@Upsert
suspend fun upsert(giftWrapSeal: at.torch.compose.database.model.GiftWrapSeal)
}

View File

@@ -1,6 +1,5 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.model.InReplyToRelation
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.Query
@@ -9,14 +8,14 @@ import androidx.room3.Upsert
@Dao
interface InReplyToRelationDao {
@Query("SELECT * FROM InReplyToRelation WHERE replyingNostrEventId = :replyingNostrEventId")
fun getRepostByReplyingNostrEventId(replyingNostrEventId: String): InReplyToRelation?
fun getRepostByReplyingNostrEventId(replyingNostrEventId: String): at.torch.compose.database.model.InReplyToRelation?
@Query("SELECT * FROM InReplyToRelation WHERE inReplyToNostrEventId = :inReplyToNostrEventId")
fun getRepostByInReplyToNostrEventId(inReplyToNostrEventId: String): InReplyToRelation?
fun getRepostByInReplyToNostrEventId(inReplyToNostrEventId: String): at.torch.compose.database.model.InReplyToRelation?
@Insert
suspend fun insert(inReplyToRelation: InReplyToRelation)
suspend fun insert(inReplyToRelation: at.torch.compose.database.model.InReplyToRelation)
@Upsert
suspend fun upsert(inReplyToRelation: InReplyToRelation)
suspend fun upsert(inReplyToRelation: at.torch.compose.database.model.InReplyToRelation)
}

View File

@@ -0,0 +1,18 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
import androidx.room3.Upsert
@Dao
interface MentionDao {
@Insert(
onConflict = IGNORE
)
suspend fun insert(mention: at.torch.compose.database.model.Mention)
@Upsert
suspend fun upsert(mention: at.torch.compose.database.model.Mention)
}

View File

@@ -1,7 +1,5 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest
import ac.cord.auxiliary.compose.database.model.SynchronizeNostrEventRequest
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
@@ -14,16 +12,16 @@ import kotlin.time.Instant
@Dao
interface NegentropySynchronizeRequestDao {
@Query("SELECT * FROM NegentropySynchronizeRequest WHERE status = :status AND createdAt > :createdAt")
fun observeNegentropySynchronizeRequestsByStatus(status: String, createdAt: Instant = Clock.System.now()): Flow<NegentropySynchronizeRequest?>
fun observeNegentropySynchronizeRequestsByStatus(status: String, createdAt: Instant = Clock.System.now()): Flow<at.torch.compose.database.model.NegentropySynchronizeRequest?>
@Query("SELECT COUNT(*) FROM NegentropySynchronizeRequest WHERE purpose = :purpose AND status IN (:status) AND createdAt > :createdAt")
fun observeNegentropySynchronizeRequestByPurposeAndStatusCount(purpose: String, status: List<String>, createdAt: Instant = Clock.System.now()): Flow<Int>
@Upsert
fun upsert(negentropySynchronizeRequest: NegentropySynchronizeRequest)
fun upsert(negentropySynchronizeRequest: at.torch.compose.database.model.NegentropySynchronizeRequest)
@Insert(
onConflict = IGNORE
)
fun insert(negentropySynchronizeRequests: List<NegentropySynchronizeRequest>)
fun insert(negentropySynchronizeRequests: List<at.torch.compose.database.model.NegentropySynchronizeRequest>)
}

View File

@@ -0,0 +1,10 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Upsert
@Dao
interface NegentropySynchronizeResultDao {
@Upsert
fun upsert(negentropySynchronizeResult: at.torch.compose.database.model.NegentropySynchronizeResult)
}

View File

@@ -1,26 +1,24 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.AuxDatabase
import ac.cord.auxiliary.compose.database.GENESIS_AT
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventReceipt
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest
import ac.cord.auxiliary.compose.database.model.ChatMessage
import ac.cord.auxiliary.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation
import ac.cord.auxiliary.compose.database.model.ChatRoom
import ac.cord.auxiliary.compose.database.model.Connection
import ac.cord.auxiliary.compose.database.model.Mention
import ac.cord.auxiliary.compose.database.model.NegentropySynchronizeRequest
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.database.model.NostrEventRelay
import ac.cord.auxiliary.compose.database.model.Participant
import ac.cord.auxiliary.compose.database.model.Post
import ac.cord.auxiliary.compose.database.model.Profile
import ac.cord.auxiliary.compose.database.model.UnsignedNostrEvent
import ac.cord.auxiliary.compose.database.model.types.SynchronizationFilter
import ac.cord.auxiliary.compose.exceptions.GiftWrapImpersonationException
import ac.cord.auxiliary.compose.exceptions.GiftWrapSealDecryptionException
import ac.cord.auxiliary.compose.exceptions.GiftWrapUnsealException
import ac.cord.auxiliary.compose.extensions.toHex
import at.torch.compose.database.GENESIS_AT
import at.torch.compose.database.model.BroadcastNostrEventReceipt
import at.torch.compose.database.model.BroadcastNostrEventRequest
import at.torch.compose.database.model.ChatMessage
import at.torch.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation
import at.torch.compose.database.model.ChatRoom
import at.torch.compose.database.model.Connection
import at.torch.compose.database.model.Mention
import at.torch.compose.database.model.NegentropySynchronizeRequest
import at.torch.compose.database.model.NostrEvent
import at.torch.compose.database.model.NostrEventRelay
import at.torch.compose.database.model.Participant
import at.torch.compose.database.model.Post
import at.torch.compose.database.model.Profile
import at.torch.compose.database.model.types.SynchronizationFilter
import at.torch.compose.exceptions.GiftWrapImpersonationException
import at.torch.compose.exceptions.GiftWrapSealDecryptionException
import at.torch.compose.exceptions.GiftWrapUnsealException
import at.torch.compose.extensions.toHex
import androidx.room3.Dao
import androidx.room3.Transaction
import co.touchlab.kermit.Logger
@@ -32,14 +30,12 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUsers
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import fr.acinq.phoenix.managers.WalletManager
import fr.acinq.phoenix.managers.nostrPrivateKey
import kotlin.time.Clock
import kotlin.time.Instant
@Dao
abstract class NostrDao(
private val database: AuxDatabase,
private val database: at.torch.compose.database.TorchDatabase,
) {
val logger = Logger.withTag("NostrDao")
@@ -56,8 +52,8 @@ abstract class NostrDao(
@Transaction
open suspend fun publishNostrEvent(
unsignedNostrEvent: UnsignedNostrEvent,
nostrEvent: NostrEvent,
unsignedNostrEvent: at.torch.compose.database.model.UnsignedNostrEvent,
nostrEvent: at.torch.compose.database.model.NostrEvent,
relayURLs: List<String>,
activeKeyPair: KeyPair
) {
@@ -81,7 +77,7 @@ abstract class NostrDao(
relayURLs.forEach { relayURL ->
database.broadcastNostrEventRequestDao().upsert(
BroadcastNostrEventRequest(
_root_ide_package_.ac.cord.compose.database.model.BroadcastNostrEventRequest(
nostrEventId = nostrEvent.id,
unsignedNostrEventId = nostrEvent.unsignedNostrEventId,
relayURL = relayURL
@@ -92,7 +88,7 @@ abstract class NostrDao(
@Transaction
open suspend fun storeNostrEvent(
nostrEvent: NostrEvent,
nostrEvent: at.torch.compose.database.model.NostrEvent,
relayURL: String,
synchronizationRelayURLs: List<String>,
level: Int,
@@ -113,7 +109,7 @@ abstract class NostrDao(
} else {
// Nothing else needs to be done
val profile = database.profileDao().getProfileByPublicKey(nostrEvent.pubKey)
if (profile?.createdAt == GENESIS_AT && nostrEvent.kind == MetadataEvent.KIND) {
if (profile?.createdAt == _root_ide_package_.ac.cord.compose.database.GENESIS_AT && nostrEvent.kind == MetadataEvent.KIND) {
logger.i("This is a placeholder profile might need to get synced...: ${profile.publicKey}")
// Setup the sync here...
} else {
@@ -122,7 +118,7 @@ abstract class NostrDao(
}
database.nostrEventRelayDao().upsert(
NostrEventRelay(
_root_ide_package_.ac.cord.compose.database.model.NostrEventRelay(
relayURL = relayURL,
nostrEventId = nostrEvent.id
)
@@ -140,7 +136,7 @@ abstract class NostrDao(
}
private suspend fun indexNostrEvent(
nostrEvent: NostrEvent,
nostrEvent: at.torch.compose.database.model.NostrEvent,
relayURL: String,
synchronizationRelayURLs: List<String>,
level: Int,
@@ -154,10 +150,10 @@ abstract class NostrDao(
val profile = database.profileDao().getProfileByPublicKey(nostrEvent.pubKey)
if (profile == null) {
val placeHolderProfile = Profile(
val placeHolderProfile = _root_ide_package_.ac.cord.compose.database.model.Profile(
displayName = "LOADING...",
publicKey = nostrEvent.pubKey,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
database.profileDao().insertPlaceholderProfile(placeHolderProfile)
@@ -165,7 +161,7 @@ abstract class NostrDao(
profilePublicKeysToSync[relayURL] = mutableSetOf()
}
profilePublicKeysToSync[relayURL]?.add(nostrEvent.pubKey)
} else if (profile.createdAt == GENESIS_AT && level == 0) {
} else if (profile.createdAt == _root_ide_package_.ac.cord.compose.database.GENESIS_AT && level == 0) {
logger.i("This is a placeholder profile... that might need to get synced...: $profile")
if (profilePublicKeysToSync[relayURL] == null) {
profilePublicKeysToSync[relayURL] = mutableSetOf()
@@ -190,7 +186,7 @@ abstract class NostrDao(
database.nostrEventDao().insert(
if (containedPost != null) {
NostrEvent(
_root_ide_package_.ac.cord.compose.database.model.NostrEvent(
id = containedPost.id,
content = containedPost.content,
createdAt = Instant.fromEpochSeconds(containedPost.createdAt),
@@ -200,10 +196,10 @@ abstract class NostrDao(
tags = containedPost.tags
)
} else {
NostrEvent(
_root_ide_package_.ac.cord.compose.database.model.NostrEvent(
id = repostedRelation.repostedNostrEventId,
content = "Pending Sync",
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
pubKey = nostrEvent.pubKey,
sig = "",
kind = nostrEvent.kind,
@@ -221,10 +217,10 @@ abstract class NostrDao(
if (repostedProfilePublicKey != null && repostedProfile == null) {
database.profileDao().insertPlaceholderProfile(
Profile(
_root_ide_package_.ac.cord.compose.database.model.Profile(
displayName = "LOADING...",
publicKey = repostedProfilePublicKey,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
)
@@ -242,10 +238,10 @@ abstract class NostrDao(
val taggedNostrEvent = database.nostrEventDao().getNostrEventById(taggedEvent.eventId)
if (taggedNostrEvent == null) {
database.nostrEventDao().insert(
NostrEvent(
_root_ide_package_.ac.cord.compose.database.model.NostrEvent(
id = taggedEvent.eventId,
content = "Pending Sync... ",
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
pubKey = nostrEvent.pubKey,
sig = "",
kind = nostrEvent.kind,
@@ -278,17 +274,17 @@ abstract class NostrDao(
if (taggedProfile == null) {
// Save a placeholder
database.profileDao().insertPlaceholderProfile(
Profile(
_root_ide_package_.ac.cord.compose.database.model.Profile(
displayName = "LOADING...",
publicKey = taggedUser.pubKey,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
)
}
database.mentionDao().insert(
Mention(
_root_ide_package_.ac.cord.compose.database.model.Mention(
mentionedPublicKey = taggedUser.pubKey,
mentioningNostrEventId = nostrEvent.id,
relayUrl = taggedUser.relayHint?.url
@@ -320,10 +316,10 @@ abstract class NostrDao(
if (giftWrapReceiverProfile == null) {
// Save a placeholder
database.profileDao().insertPlaceholderProfile(
Profile(
_root_ide_package_.ac.cord.compose.database.model.Profile(
displayName = "LOADING...",
publicKey = giftWrapMessage.receiverPublicKey,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
)
@@ -548,7 +544,7 @@ abstract class NostrDao(
activeKeyPair
).let { giftWrapSeal ->
if (giftWrapSeal == null) {
throw GiftWrapUnsealException("Failed to unseal ${nostrEvent.id} from the giftWrap")
throw _root_ide_package_.ac.cord.compose.exceptions.GiftWrapUnsealException("Failed to unseal ${nostrEvent.id} from the giftWrap")
} else {
database.giftWrapSealDao().upsert(
giftWrapSeal
@@ -558,12 +554,15 @@ abstract class NostrDao(
activeKeyPair.privKey!!
).let { decryptedGiftWrapPayload ->
if (decryptedGiftWrapPayload == null) {
throw GiftWrapSealDecryptionException("Failed to decrypt sealed ${giftWrapSeal.id} payload")
throw _root_ide_package_.ac.cord.compose.exceptions.GiftWrapSealDecryptionException(
"Failed to decrypt sealed ${giftWrapSeal.id} payload"
)
} else {
if (giftWrapSeal.publicKey.lowercase() != decryptedGiftWrapPayload.publicKey.lowercase()) {
val giftWrapImpersonation = GiftWrapImpersonationException(
"Seal pubkey (${giftWrapSeal.publicKey}) and payload pubkey (${decryptedGiftWrapPayload.publicKey}) needs to be the same for the giftWrap ${nostrEvent.id}"
)
val giftWrapImpersonation =
_root_ide_package_.ac.cord.compose.exceptions.GiftWrapImpersonationException(
"Seal pubkey (${giftWrapSeal.publicKey}) and payload pubkey (${decryptedGiftWrapPayload.publicKey}) needs to be the same for the giftWrap ${nostrEvent.id}"
)
logger.e("Impersonation", giftWrapImpersonation)
throw giftWrapImpersonation // Once this is thrown the database transaction fails and nothing gets saved to the DB...
}
@@ -592,7 +591,7 @@ abstract class NostrDao(
if (localChatRoom == null) {
val userPublicKey = activeKeyPair.pubKey.toHex()
database.chatRoomDao().upsert(
ChatRoom(
_root_ide_package_.ac.cord.compose.database.model.ChatRoom(
id = chatRoomId,
userPublicKey = userPublicKey,
subject = decryptedGiftWrapPayload.parseSubject(),
@@ -605,7 +604,7 @@ abstract class NostrDao(
val participants = giftWrapPayload.participantPTags(
NormalizedRelayUrl(relayURL) // TODO: Get relayURL for publicKey profile...
).map {
Participant(
_root_ide_package_.ac.cord.compose.database.model.Participant(
participantPublicKey = it.pubKey,
chatRoomId = chatRoomId,
relayHint = it.relayHint?.url
@@ -621,10 +620,10 @@ abstract class NostrDao(
if (giftWrapParticipantProfile == null) {
// Save a placeholder
database.profileDao().insertPlaceholderProfile(
Profile(
_root_ide_package_.ac.cord.compose.database.model.Profile(
displayName = "LOADING...",
publicKey = participant.participantPublicKey,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
)
@@ -669,7 +668,7 @@ abstract class NostrDao(
authors = arrayOf(
publicKey
),
since = GENESIS_AT,
since = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
limit = 5
).firstOrNull()?.let { nostrEvent ->
if (nostrEvent.kind != ChatMessageRelayListEvent.KIND) {
@@ -690,7 +689,7 @@ abstract class NostrDao(
if (chatMessageRelayListEvent != null) {
// Sync messages from this relay that were sent by us
val synchronizationFilter =
SynchronizationFilter(
_root_ide_package_.ac.cord.compose.database.model.types.SynchronizationFilter(
kinds = arrayOf(
GiftWrapEvent.KIND,
),
@@ -709,8 +708,8 @@ abstract class NostrDao(
.insert(
chatMessageRelayListEvent.relays()
.map { normalizedRelayUrl ->
NegentropySynchronizeRequest(
id = NegentropySynchronizeRequest.computeId(
_root_ide_package_.ac.cord.compose.database.model.NegentropySynchronizeRequest(
id = _root_ide_package_.at.torch.compose.database.model.NegentropySynchronizeRequest.Companion.computeId(
relayURL = normalizedRelayUrl.url,
synchronizationFilter = synchronizationFilter
),
@@ -750,7 +749,7 @@ abstract class NostrDao(
}
val chatMessageId = database.chatMessageDao().upsert(
ChatMessage(
_root_ide_package_.ac.cord.compose.database.model.ChatMessage(
giftWrapPayloadId = giftWrapPayload.id,
senderPublicKey = giftWrapPayload.publicKey,
isUserMessage = activeKeyPair.pubKey.toHex() == giftWrapPayload.publicKey,
@@ -762,7 +761,7 @@ abstract class NostrDao(
val broadcastNostrEventReceiptId =
database.broadcastNostrEventReceiptDao().upsert(
BroadcastNostrEventReceipt(
_root_ide_package_.ac.cord.compose.database.model.BroadcastNostrEventReceipt(
nostrEventId = nostrEvent.id,
isAccepted = true,
isSync = true,
@@ -772,7 +771,7 @@ abstract class NostrDao(
database.chatMessageBroadcastNostrEventReceiptRelationDao()
.upsert(
ChatMessageBroadcastNostrEventReceiptRelation(
_root_ide_package_.ac.cord.compose.database.model.ChatMessageBroadcastNostrEventReceiptRelation(
broadcastNostrEventReceiptId = broadcastNostrEventReceiptId,
chatMessageId = chatMessageId
)
@@ -795,9 +794,9 @@ abstract class NostrDao(
// Persist PlaceHolder and sync
// TODO: Get replyingToAuthorPublicKey
database.postDao().insert(
Post(
_root_ide_package_.ac.cord.compose.database.model.Post(
id = post.replyToId,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync handled by tagged events...
content = post.replyToId,
profilePublicKey = nostrEvent.pubKey // Will get overwriting by sync handled by tagged events...
@@ -815,9 +814,9 @@ abstract class NostrDao(
if (replyingToPost == null) {
// Persist PlaceHolder and sync
database.postDao().insert(
Post(
_root_ide_package_.ac.cord.compose.database.model.Post(
id = reaction.replyToId,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync handled by tagged events...
content = reaction.replyToId,
profilePublicKey = nostrEvent.pubKey // Will get overwriting by sync handled by tagged events...
@@ -837,10 +836,10 @@ abstract class NostrDao(
eventIdsToSync[relayURL]?.add(quotedRelation.quotedNostrEventId)
database.nostrEventDao().insert(
NostrEvent(
_root_ide_package_.ac.cord.compose.database.model.NostrEvent(
id = quotedRelation.quotedNostrEventId,
content = "Pending Sync",
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
pubKey = nostrEvent.pubKey,
sig = "",
kind = nostrEvent.kind,
@@ -857,10 +856,10 @@ abstract class NostrDao(
if (quotedProfilePublicKey != null && quotedProfile == null) {
database.profileDao().insertPlaceholderProfile(
Profile(
_root_ide_package_.ac.cord.compose.database.model.Profile(
displayName = "LOADING...",
publicKey = quotedProfilePublicKey,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
)
@@ -885,10 +884,10 @@ abstract class NostrDao(
eventIdsToSync[relayURL]?.add(inReplyToRelation.inReplyToNostrEventId)
database.nostrEventDao().insert(
NostrEvent(
_root_ide_package_.ac.cord.compose.database.model.NostrEvent(
id = inReplyToRelation.inReplyToNostrEventId,
content = "Pending Sync",
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
pubKey = nostrEvent.pubKey,
sig = "",
kind = nostrEvent.kind,
@@ -919,10 +918,10 @@ abstract class NostrDao(
if (inReplyToRootProfilePublicKey != null && inReplyToProfile == null) {
database.profileDao().insertPlaceholderProfile(
Profile(
_root_ide_package_.ac.cord.compose.database.model.Profile(
displayName = "LOADING...",
publicKey = inReplyToRootProfilePublicKey,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
)
@@ -943,9 +942,9 @@ abstract class NostrDao(
if (zappedPost == null) {
// Persist PlaceHolder and sync
database.postDao().insert(
Post(
_root_ide_package_.ac.cord.compose.database.model.Post(
id = zap.postId,
createdAt = GENESIS_AT,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
content = zap.postId,
profilePublicKey = nostrEvent.pubKey // Will get overwriting by sync,
@@ -979,18 +978,19 @@ abstract class NostrDao(
val profile = database.profileDao().getProfileByPublicKey(followedHexKEys)
if (profile == null) {
val placeHolderProfile = Profile(
displayName = "LOADING...",
publicKey = followedHexKEys,
createdAt = GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
val placeHolderProfile =
_root_ide_package_.ac.cord.compose.database.model.Profile(
displayName = "LOADING...",
publicKey = followedHexKEys,
createdAt = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
nostrEventId = nostrEvent.id, // Will get overwriting by sync,
)
database.profileDao().insertPlaceholderProfile(placeHolderProfile)
if (profilePublicKeysToSync[relayURL] == null) {
profilePublicKeysToSync[relayURL] = mutableSetOf()
}
profilePublicKeysToSync[relayURL]?.add(followedHexKEys)
} else if (profile.createdAt == GENESIS_AT && level == 0) {
} else if (profile.createdAt == _root_ide_package_.ac.cord.compose.database.GENESIS_AT && level == 0) {
logger.i("${level} This is a placeholder profile... that might need to get synced...: $profile")
if (profilePublicKeysToSync[relayURL] == null) {
@@ -1001,7 +1001,7 @@ abstract class NostrDao(
// Save profile connections...
database.connectionDao().upsert(
Connection(
_root_ide_package_.ac.cord.compose.database.model.Connection(
sourcePublicKey = nostrEvent.pubKey,
destinationPublicKey = followedHexKEys,
nostrEventId = nostrEvent.id
@@ -1012,15 +1012,16 @@ abstract class NostrDao(
}
if (level == 0) {
val negentropySynchronizeRequests = mutableListOf<NegentropySynchronizeRequest>()
val negentropySynchronizeRequests = mutableListOf<at.torch.compose.database.model.NegentropySynchronizeRequest>()
profilePublicKeysToSync.forEach { relayUrlProfilePublicKey ->
val synchronizationFilter = SynchronizationFilter(
authors = relayUrlProfilePublicKey.value.toTypedArray(),
kinds = profileEventKinds
)
val synchronizationFilter =
_root_ide_package_.ac.cord.compose.database.model.types.SynchronizationFilter(
authors = relayUrlProfilePublicKey.value.toTypedArray(),
kinds = profileEventKinds
)
negentropySynchronizeRequests.add(
NegentropySynchronizeRequest(
id = NegentropySynchronizeRequest.computeId(
_root_ide_package_.ac.cord.compose.database.model.NegentropySynchronizeRequest(
id = _root_ide_package_.at.torch.compose.database.model.NegentropySynchronizeRequest.Companion.computeId(
relayUrlProfilePublicKey.key,
synchronizationFilter
),
@@ -1034,13 +1035,14 @@ abstract class NostrDao(
eventIdsToSync.forEach { relayUrlEventIds ->
// TODO: Hash relayUrl + synchronicationFilter + ephocMinutes
val synchronizationFilter = SynchronizationFilter(
ids = relayUrlEventIds.value.toTypedArray(),
)
val synchronizationFilter =
_root_ide_package_.ac.cord.compose.database.model.types.SynchronizationFilter(
ids = relayUrlEventIds.value.toTypedArray(),
)
Clock.System.now().toEpochMilliseconds()
negentropySynchronizeRequests.add(
NegentropySynchronizeRequest(
id = NegentropySynchronizeRequest.computeId(
_root_ide_package_.ac.cord.compose.database.model.NegentropySynchronizeRequest(
id = _root_ide_package_.at.torch.compose.database.model.NegentropySynchronizeRequest.Companion.computeId(
relayUrlEventIds.key,
synchronizationFilter
),

View File

@@ -1,8 +1,5 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.GENESIS_AT
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalNostrEvent
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
@@ -13,7 +10,6 @@ import androidx.room3.Transaction
import androidx.room3.Upsert
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Kind
import fr.acinq.bitcoin.PublicKey
import kotlinx.coroutines.flow.Flow
import kotlin.time.Instant
@@ -21,35 +17,35 @@ import kotlin.time.Instant
interface NostrEventDao {
@Transaction
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) ORDER BY createdAt DESC")
fun observeNostrEvents(kinds: Array<Kind>): Flow<List<LocalNostrEvent>>
fun observeNostrEvents(kinds: Array<Kind>): Flow<List<at.torch.compose.database.model.intermdiate.LocalNostrEvent>>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE content LIKE '%' || :search || '%' AND kind in (:kinds) ORDER BY createdAt DESC")
fun observeFilteredNostrEvents(
kinds: Array<Kind>,
search: String
): Flow<List<LocalNostrEvent>>
): Flow<List<at.torch.compose.database.model.intermdiate.LocalNostrEvent>>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND id in (:ids) ORDER BY createdAt DESC")
fun observeFilteredNostrEvents(
kinds: Array<Kind>,
ids: Array<HexKey>,
): Flow<List<LocalNostrEvent>>
): Flow<List<at.torch.compose.database.model.intermdiate.LocalNostrEvent>>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND pubKey in (:authors) ORDER BY createdAt DESC LIMIT 50")
fun observeAuthoredNostrEvents(
kinds: Array<Kind>,
authors: Array<HexKey>,
): Flow<List<LocalNostrEvent>>
): Flow<List<at.torch.compose.database.model.intermdiate.LocalNostrEvent>>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE tags LIKE '%' || :publicKey || '%' AND kind in (:kinds) ORDER BY createdAt DESC")
fun observePublicKeyMentionedNostrEvents(
kinds: Array<Kind>,
publicKey: HexKey
): Flow<List<LocalNostrEvent>>
): Flow<List<at.torch.compose.database.model.intermdiate.LocalNostrEvent>>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE tags LIKE '%' || :eventId || '%reply%' AND kind in (:kinds) AND createdAt > :since ORDER BY createdAt DESC")
@@ -57,13 +53,13 @@ interface NostrEventDao {
kinds: Array<Kind>,
eventId: HexKey,
since: Instant
): Flow<List<LocalNostrEvent>>
): Flow<List<at.torch.compose.database.model.intermdiate.LocalNostrEvent>>
@Transaction
@RawQuery
fun observePublicKeyFollowingNostrEvents(
query: RoomRawQuery
): Flow<List<LocalNostrEvent>>
): Flow<List<at.torch.compose.database.model.intermdiate.LocalNostrEvent>>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND createdAt > :since ORDER BY createdAt ASC LIMIT :limit")
@@ -71,7 +67,7 @@ interface NostrEventDao {
kinds: Array<Kind>,
since: Instant,
limit: Int
): List<NostrEvent>
): List<at.torch.compose.database.model.NostrEvent>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE content LIKE '%' || :search || '%' AND kind in (:kinds) AND createdAt > :since ORDER BY createdAt ASC LIMIT :limit")
@@ -80,7 +76,7 @@ interface NostrEventDao {
search: String,
since: Instant,
limit: Int
): List<NostrEvent>
): List<at.torch.compose.database.model.NostrEvent>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND id in (:ids) AND createdAt > :since ORDER BY createdAt ASC LIMIT :limit")
@@ -89,7 +85,7 @@ interface NostrEventDao {
ids: Array<HexKey>,
since: Instant,
limit: Int
): List<NostrEvent>
): List<at.torch.compose.database.model.NostrEvent>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE id in (:ids) AND createdAt > :since ORDER BY createdAt ASC LIMIT :limit")
@@ -97,7 +93,7 @@ interface NostrEventDao {
ids: Array<HexKey>,
since: Instant,
limit: Int
): List<NostrEvent>
): List<at.torch.compose.database.model.NostrEvent>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) AND pubKey in (:authors) AND createdAt > :since ORDER BY createdAt ASC LIMIT :limit")
@@ -106,7 +102,7 @@ interface NostrEventDao {
authors: Array<HexKey>,
since: Instant,
limit: Int
): List<NostrEvent>
): List<at.torch.compose.database.model.NostrEvent>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE tags LIKE '%' || :publicKey || '%' AND kind in (:kinds) AND createdAt > :since ORDER BY createdAt ASC LIMIT :limit")
@@ -115,7 +111,7 @@ interface NostrEventDao {
publicKey: HexKey,
since: Instant,
limit: Int
): List<NostrEvent>
): List<at.torch.compose.database.model.NostrEvent>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE tags LIKE '%' || :eventId || '%reply%' AND kind in (:kinds) AND createdAt > :since ORDER BY createdAt ASC LIMIT :limit")
@@ -124,26 +120,26 @@ interface NostrEventDao {
eventId: HexKey,
since: Instant,
limit: Int
): List<NostrEvent>
): List<at.torch.compose.database.model.NostrEvent>
@Query("SELECT * FROM NostrEvent WHERE kind in (:kinds) ORDER BY savedAt DESC")
fun getAllNostrEvents(kinds: Array<Kind>): List<NostrEvent>
fun getAllNostrEvents(kinds: Array<Kind>): List<at.torch.compose.database.model.NostrEvent>
@Transaction
@Query("SELECT * FROM NostrEvent WHERE id = :id")
fun observeNostrEventById(id: String): Flow<LocalNostrEvent?>
fun observeNostrEventById(id: String): Flow<at.torch.compose.database.model.intermdiate.LocalNostrEvent?>
@Query("SELECT * FROM NostrEvent WHERE id = :id")
fun getNostrEventById(id: String): NostrEvent?
fun getNostrEventById(id: String): at.torch.compose.database.model.NostrEvent?
@Query("SELECT * FROM NostrEvent WHERE pubKey = :publicKey AND kind = :kind ORDER BY createdAt DESC")
fun getNostrEventByPublicKeyAndKind(publicKey: HexKey, kind: Kind): NostrEvent?
fun getNostrEventByPublicKeyAndKind(publicKey: HexKey, kind: Kind): at.torch.compose.database.model.NostrEvent?
@Upsert
suspend fun upsert(nostrEvent: NostrEvent)
suspend fun upsert(nostrEvent: at.torch.compose.database.model.NostrEvent)
@Insert(
onConflict = IGNORE
)
suspend fun insert(nostrEvent: NostrEvent)
suspend fun insert(nostrEvent: at.torch.compose.database.model.NostrEvent)
}

View File

@@ -0,0 +1,12 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Upsert
@Dao
interface NostrEventRelayDao {
@Upsert
suspend fun upsert(nostrEventRelay: at.torch.compose.database.model.NostrEventRelay)
}

View File

@@ -1,38 +1,34 @@
package ac.cord.auxiliary.compose.database.dao
package at.torch.compose.database.dao
import ac.cord.auxiliary.compose.database.AuxDatabase
import ac.cord.auxiliary.compose.database.GENESIS_AT
import ac.cord.auxiliary.compose.database.model.BroadcastNostrEventRequest
import ac.cord.auxiliary.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
import ac.cord.auxiliary.compose.database.model.ChatMessageNostrEventRelation
import ac.cord.auxiliary.compose.database.model.ChatRoom
import ac.cord.auxiliary.compose.database.model.GiftWrapMessage
import ac.cord.auxiliary.compose.database.model.GiftWrapPayload
import ac.cord.auxiliary.compose.database.model.GiftWrapSeal
import ac.cord.auxiliary.compose.database.model.NostrEvent
import ac.cord.auxiliary.compose.database.model.Participant
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
import at.torch.compose.database.GENESIS_AT
import at.torch.compose.database.model.BroadcastNostrEventRequest
import at.torch.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation
import at.torch.compose.database.model.ChatMessageNostrEventRelation
import at.torch.compose.database.model.ChatRoom
import at.torch.compose.database.model.GiftWrapMessage
import at.torch.compose.database.model.GiftWrapSeal
import at.torch.compose.database.model.NostrEvent
import at.torch.compose.database.model.Participant
import at.torch.compose.database.model.intermdiate.LocalChatRoom
import androidx.room3.Dao
import androidx.room3.Transaction
import co.touchlab.kermit.Logger
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import kotlin.math.log
import kotlin.time.Instant
@Dao
abstract class NostrNip17Dao(
private val database: AuxDatabase
private val database: at.torch.compose.database.TorchDatabase
) {
val logger = Logger.withTag(TAG)
@Transaction
open suspend fun getOrCreateChatRoom(activeUserPublicKey: HexKey, publicKey: String, relayHint: String?, defaultSubject: String? = null): LocalChatRoom? {
open suspend fun getOrCreateChatRoom(activeUserPublicKey: HexKey, publicKey: String, relayHint: String?, defaultSubject: String? = null): at.torch.compose.database.model.intermdiate.LocalChatRoom? {
logger.d("getOrCreateChatRoom: $publicKey")
@@ -41,7 +37,7 @@ abstract class NostrNip17Dao(
publicKey
)
val chatRoomId = ChatRoom.deriveChatRoomId(hexKeys)
val chatRoomId = _root_ide_package_.at.torch.compose.database.model.ChatRoom.Companion.deriveChatRoomId(hexKeys)
logger.d("chatRoomId: $chatRoomId")
val localChatRoom = database.chatRoomDao().findChatRoomById(chatRoomId)
@@ -52,7 +48,7 @@ abstract class NostrNip17Dao(
if (profiles.isNotEmpty()) {
// Create new chatRoom
val chatRoom = ChatRoom(
val chatRoom = _root_ide_package_.ac.cord.compose.database.model.ChatRoom(
id = chatRoomId,
userPublicKey = activeUserPublicKey,
subject = defaultSubject,
@@ -61,7 +57,7 @@ abstract class NostrNip17Dao(
database.participantDao().upsert(
profiles.map {
Participant(
_root_ide_package_.ac.cord.compose.database.model.Participant(
participantPublicKey = it.publicKey,
chatRoomId = chatRoomId,
relayHint = relayHint
@@ -69,7 +65,7 @@ abstract class NostrNip17Dao(
}
)
return LocalChatRoom(
return _root_ide_package_.ac.cord.compose.database.model.intermdiate.LocalChatRoom(
chatRoom = chatRoom
)
}
@@ -80,12 +76,12 @@ abstract class NostrNip17Dao(
@Transaction
open suspend fun persistAndBroadcastGiftWrap(
giftWrapPayload: Pair<Int, GiftWrapPayload>,
giftWrapPayload: Pair<Int, at.torch.compose.database.model.GiftWrapPayload>,
sealedRumorEvent: SealedRumorEvent,
giftWrapEvent: GiftWrapEvent,
receiverPublicKey: PTag
) {
val nostrEvent = NostrEvent(
val nostrEvent = _root_ide_package_.ac.cord.compose.database.model.NostrEvent(
id = giftWrapEvent.id,
content = giftWrapEvent.content,
createdAt = Instant.fromEpochSeconds(giftWrapEvent.createdAt),
@@ -103,7 +99,7 @@ abstract class NostrNip17Dao(
chatMessage?.let {
database.chatMessageNostrEventRelationDao().upsert(
ChatMessageNostrEventRelation(
_root_ide_package_.ac.cord.compose.database.model.ChatMessageNostrEventRelation(
chatMessageId = it.id,
nostrEventId = nostrEvent.id
)
@@ -116,7 +112,7 @@ abstract class NostrNip17Dao(
authors = arrayOf(
receiverPublicKey.pubKey
),
since = GENESIS_AT,
since = _root_ide_package_.ac.cord.compose.database.GENESIS_AT,
limit = 5
)
logger.d("Found messageRelayLists: $chatMessageRelayListEvents")
@@ -138,7 +134,7 @@ abstract class NostrNip17Dao(
logger.d("Submit BroadcastNostrEventRequest to $relayUrl")
val broadcastNostrEventRequestIds = database.broadcastNostrEventRequestDao().insert(
listOf(
BroadcastNostrEventRequest(
_root_ide_package_.ac.cord.compose.database.model.BroadcastNostrEventRequest(
nostrEventId = nostrEvent.id,
relayURL = relayUrl.url
)
@@ -148,7 +144,7 @@ abstract class NostrNip17Dao(
chatMessage?.let {
broadcastNostrEventRequestIds.forEach {
database.chatMessageBroadcastNostrEventRequestRelationDao().upsert(
ChatMessageBroadcastNostrEventRequestRelation(
_root_ide_package_.ac.cord.compose.database.model.ChatMessageBroadcastNostrEventRequestRelation(
chatMessageId = chatMessage.id,
broadcastNostrEventRequestId = it
)
@@ -163,7 +159,7 @@ abstract class NostrNip17Dao(
database.giftWrapMessageDao().upsert(
GiftWrapMessage(
_root_ide_package_.ac.cord.compose.database.model.GiftWrapMessage(
id = giftWrapEvent.id,
nostrEventId = nostrEvent.id,
content = giftWrapEvent.content,
@@ -177,7 +173,7 @@ abstract class NostrNip17Dao(
)
database.giftWrapSealDao().upsert(
GiftWrapSeal(
_root_ide_package_.ac.cord.compose.database.model.GiftWrapSeal(
id = sealedRumorEvent.id,
publicKey = sealedRumorEvent.pubKey,
content = sealedRumorEvent.content,

View File

@@ -0,0 +1,18 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Query
import androidx.room3.Upsert
@Dao
interface ParticipantDao {
@Query("SELECT * FROM Participant WHERE participantPublicKey = :participantPublicKey")
fun findParticipantByPublicKey(participantPublicKey: String): at.torch.compose.database.model.Participant?
@Query("SELECT * FROM Participant WHERE chatRoomId = :chatRoomId")
fun findParticipantsByChatRoomId(chatRoomId: String): List<at.torch.compose.database.model.Participant>
@Upsert
suspend fun upsert(participants: List<at.torch.compose.database.model.Participant>)
}

View File

@@ -0,0 +1,21 @@
package at.torch.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.Query
import androidx.room3.Upsert
@Dao
interface PostDao {
@Query("SELECT * FROM Post")
fun getAllPosts(): List<at.torch.compose.database.model.intermdiate.LocalPost>
@Query("SELECT * FROM Post WHERE id = :id")
fun getPostById(id: String): at.torch.compose.database.model.Post?
@Insert
suspend fun insert(post: at.torch.compose.database.model.Post)
@Upsert
suspend fun upsert(post: at.torch.compose.database.model.Post)
}

Some files were not shown because too many files have changed in this diff Show More