diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fe63bb677..148fdd246 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8f1ca7d69..d4b1f7955 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -143,7 +143,7 @@ android { composeOptions { // Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin - kotlinCompilerExtensionVersion "1.5.11" + kotlinCompilerExtensionVersion "1.5.14" } packagingOptions { resources { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 8e84dee15..86e0cb618 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -2161,17 +2161,6 @@ class Account( return LocalCache.getOrCreateAddressableNote(aTag) } - fun getFileServersNote(): AddressableNote { - val aTag = - ATag( - FileServersEvent.KIND, - userProfile().pubkeyHex, - "", - null, - ) - return LocalCache.getOrCreateAddressableNote(aTag) - } - fun getBlockList(): PeopleListEvent? { return getBlockListNote().event as? PeopleListEvent } @@ -2180,10 +2169,6 @@ class Account( return getMuteListNote().event as? MuteListEvent } - fun getFileServersList(): FileServersEvent? { - return getFileServersNote().event as? FileServersEvent - } - fun hideWord(word: String) { val muteList = getMuteList() @@ -2688,10 +2673,7 @@ class Account( fun saveSearchRelayList(searchRelays: List) { if (!isWriteable()) return - val relayListForSearch = - LocalCache.getOrCreateAddressableNote( - SearchRelayListEvent.createAddressATag(signer.pubKey), - ).event as? SearchRelayListEvent + val relayListForSearch = getSearchRelayList() if (relayListForSearch != null && relayListForSearch.tags.isNotEmpty()) { SearchRelayListEvent.updateRelayList( @@ -2730,10 +2712,7 @@ class Account( fun sendNip65RelayList(relays: List) { if (!isWriteable()) return - val nip65RelayList = - LocalCache.getOrCreateAddressableNote( - AdvertisedRelayListEvent.createAddressATag(signer.pubKey), - ).event as? AdvertisedRelayListEvent + val nip65RelayList = getNIP65RelayList() if (nip65RelayList != null) { AdvertisedRelayListEvent.updateRelayList( @@ -2755,6 +2734,43 @@ class Account( } } + fun getFileServersList(): FileServersEvent? { + return getFileServersNote().event as? FileServersEvent + } + + fun getFileServersListFlow(): StateFlow { + return getFileServersNote().flow().metadata.stateFlow + } + + fun getFileServersNote(): AddressableNote { + return LocalCache.getOrCreateAddressableNote(FileServersEvent.createAddressATag(userProfile().pubkeyHex)) + } + + fun sendFileServersList(servers: List) { + if (!isWriteable()) return + + val serverList = getFileServersList() + + if (serverList != null && serverList.tags.isNotEmpty()) { + FileServersEvent.updateRelayList( + earlierVersion = serverList, + relays = servers, + signer = signer, + ) { + Client.send(it) + LocalCache.justConsume(it, null) + } + } else { + FileServersEvent.createFromScratch( + relays = servers, + signer = signer, + ) { + Client.send(it) + LocalCache.justConsume(it, null) + } + } + } + fun setHideDeleteRequestDialog() { hideDeleteRequestDialog = true saveable.invalidateData() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt index b63052cb2..d7dfc7268 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/service/NostrAccountDataSource.kt @@ -45,6 +45,7 @@ import com.vitorpamplona.quartz.events.DraftEvent import com.vitorpamplona.quartz.events.EmojiPackSelectionEvent import com.vitorpamplona.quartz.events.Event import com.vitorpamplona.quartz.events.EventInterface +import com.vitorpamplona.quartz.events.FileServersEvent import com.vitorpamplona.quartz.events.GenericRepostEvent import com.vitorpamplona.quartz.events.GiftWrapEvent import com.vitorpamplona.quartz.events.GitIssueEvent @@ -123,6 +124,7 @@ object NostrAccountDataSource : NostrDataSource("AccountData") { AdvertisedRelayListEvent.KIND, ChatMessageRelayListEvent.KIND, SearchRelayListEvent.KIND, + FileServersEvent.KIND, MuteListEvent.KIND, PeopleListEvent.KIND, ), diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt index 420daa637..c37b11250 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomListScreen.kt @@ -42,7 +42,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text -import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect @@ -80,7 +79,6 @@ import com.vitorpamplona.amethyst.ui.theme.placeholderText import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3WindowSizeClassApi::class) @Composable fun ChatroomListScreen( knownFeedViewModel: NostrChatroomListKnownFeedViewModel, diff --git a/commons/build.gradle b/commons/build.gradle index c139a70c8..21921a446 100644 --- a/commons/build.gradle +++ b/commons/build.gradle @@ -38,7 +38,7 @@ android { } composeOptions { // Should match compose version : https://developer.android.com/jetpack/androidx/releases/compose-kotlin - kotlinCompilerExtensionVersion "1.5.11" + kotlinCompilerExtensionVersion "1.5.14" } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 90369520a..53b76b619 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,11 +3,10 @@ accompanistAdaptive = "0.34.0" activityCompose = "1.9.0" agp = "8.4.1" androidKotlinGeohash = "1.0" -androidLifecycle = "2.7.0" androidxJunit = "1.2.0-beta01" -appcompat = "1.6.1" +appcompat = "1.7.0" audiowaveform = "1.1.1" -benchmark = "1.2.3" +benchmark = "1.2.4" benchmarkJunit4 = "1.2.4" biometricKtx = "1.2.0-alpha05" blurhash = "1.0.0" @@ -21,10 +20,11 @@ gms = "4.4.1" jacksonModuleKotlin = "2.17.1" jna = "5.14.0" junit = "4.13.2" -kotlin = "1.9.23" +kotlin = "1.9.24" kotlinxCollectionsImmutable = "0.3.7" languageId = "17.0.5" lazysodiumAndroid = "5.1.0" +lifecycleRuntimeKtx = "2.7.0" lightcompressor = "1.3.2" markdown = "077a2cde64" media3 = "1.3.1" @@ -59,10 +59,10 @@ androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", ve androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxJunit" } androidx-junit-ktx = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxJunit" } -androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidLifecycle" } -androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidLifecycle" } -androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidLifecycle" } -androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidLifecycle" } +androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycleRuntimeKtx" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" } androidx-material-icons = { group = "androidx.compose.material", name = "material-icons-extended" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3-windowSize = { group = "androidx.compose.material3", name = "material3-window-size-class" } diff --git a/quartz/src/main/java/com/vitorpamplona/quartz/events/FileServersEvent.kt b/quartz/src/main/java/com/vitorpamplona/quartz/events/FileServersEvent.kt index a2825b805..2bdbcab03 100644 --- a/quartz/src/main/java/com/vitorpamplona/quartz/events/FileServersEvent.kt +++ b/quartz/src/main/java/com/vitorpamplona/quartz/events/FileServersEvent.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.quartz.events import androidx.compose.runtime.Immutable +import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.utils.TimeUtils @@ -36,24 +37,68 @@ class FileServersEvent( ) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) { override fun dTag() = FIXED_D_TAG + fun servers(): List { + return tags.mapNotNull { + if (it.size > 1 && it[0] == "relay") { + it[1] + } else { + null + } + } + } + companion object { const val KIND = 10096 const val FIXED_D_TAG = "" const val ALT = "File servers used by the author" - fun create( - listOfServers: List, + fun createAddressATag(pubKey: HexKey): ATag { + return ATag(KIND, pubKey, FIXED_D_TAG, null) + } + + fun createAddressTag(pubKey: HexKey): String { + return ATag.assembleATag(KIND, pubKey, FIXED_D_TAG) + } + + fun createTagArray(servers: List): Array> { + return servers.map { + arrayOf("server", it) + }.plusElement(arrayOf("alt", "Relay list to use for Search")).toTypedArray() + } + + fun updateRelayList( + earlierVersion: FileServersEvent, + relays: List, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (FileServersEvent) -> Unit, ) { - val msg = "" - val tags = mutableListOf>() + val tags = + earlierVersion.tags.filter { it[0] != "server" }.plus( + relays.map { + arrayOf("server", it) + }, + ).toTypedArray() - listOfServers.forEach { tags.add(arrayOf("server", it)) } - tags.add(arrayOf("alt", ALT)) + signer.sign(createdAt, KIND, tags, earlierVersion.content, onReady) + } - signer.sign(createdAt, KIND, tags.toTypedArray(), msg, onReady) + fun createFromScratch( + relays: List, + signer: NostrSigner, + createdAt: Long = TimeUtils.now(), + onReady: (FileServersEvent) -> Unit, + ) { + create(relays, signer, createdAt, onReady) + } + + fun create( + servers: List, + signer: NostrSigner, + createdAt: Long = TimeUtils.now(), + onReady: (FileServersEvent) -> Unit, + ) { + signer.sign(createdAt, KIND, createTagArray(servers), "", onReady) } } }