diff --git a/README.md b/README.md
index 851ee8374..1b6b6ac5d 100644
--- a/README.md
+++ b/README.md
@@ -28,9 +28,6 @@ alt="Get it on Obtaininum"
height="70">](https://github.com/ImranR98/Obtainium)
[
](https://github.com/vitorpamplona/amethyst/releases)
-[
](https://f-droid.org/packages/com.vitorpamplona.amethyst/)
[
](https://play.google.com/store/apps/details?id=com.vitorpamplona.amethyst)
diff --git a/amethyst/google-services.json b/amethyst/google-services.json
index 31ca5e8ec..dbdb975e5 100644
--- a/amethyst/google-services.json
+++ b/amethyst/google-services.json
@@ -2,7 +2,7 @@
"project_info": {
"project_number": "768341258853",
"project_id": "amethyst-3057a",
- "storage_bucket": "amethyst-3057a.appspot.com"
+ "storage_bucket": "amethyst-3057a.firebasestorage.app"
},
"client": [
{
@@ -34,6 +34,35 @@
}
}
},
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:768341258853:android:3270514bee61de546b8c8c",
+ "android_client_info": {
+ "package_name": "com.vitorpamplona.amethyst.benchmark"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "768341258853-6um8ig59qstvio60gfo60fe5e45lnqqe.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyB7ZxgdpgrN6R223HCFdfv4ulP8Egp7trE"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "768341258853-6um8ig59qstvio60gfo60fe5e45lnqqe.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ]
+ }
+ }
+ },
{
"client_info": {
"mobilesdk_app_id": "1:768341258853:android:e0200680f552484d6b8c8c",
@@ -62,35 +91,6 @@
]
}
}
- },
- {
- "client_info": {
- "mobilesdk_app_id": "1:768341258853:android:5d07c35a37b24ff36b8c8c",
- "android_client_info": {
- "package_name": "com.vitorpamplona.amethyst.benchmark"
- }
- },
- "oauth_client": [
- {
- "client_id": "768341258853-6um8ig59qstvio60gfo60fe5e45lnqqe.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyB7ZxgdpgrN6R223HCFdfv4ulP8Egp7trE"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "768341258853-6um8ig59qstvio60gfo60fe5e45lnqqe.apps.googleusercontent.com",
- "client_type": 3
- }
- ]
- }
- }
}
],
"configuration_version": "1"
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt
index a965161d4..ab8bb52bf 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt
@@ -127,8 +127,6 @@ import com.vitorpamplona.quartz.nip19Bech32.entities.NPub
import com.vitorpamplona.quartz.nip19Bech32.entities.NRelay
import com.vitorpamplona.quartz.nip19Bech32.entities.NSec
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
-import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
-import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
import com.vitorpamplona.quartz.nip30CustomEmoji.EmojiUrlTag
import com.vitorpamplona.quartz.nip30CustomEmoji.emojis
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
@@ -2313,10 +2311,29 @@ class Account(
fun signAndSendPrivately(
template: EventTemplate,
relayList: List,
+ onDone: (T) -> Unit = {},
) {
signer.sign(template) {
LocalCache.justConsume(it, null)
Amethyst.instance.client.sendPrivately(it, relayList = convertRelayList(relayList))
+ onDone(it)
+ }
+ }
+
+ fun signAndSendPrivatelyOrBroadcast(
+ template: EventTemplate,
+ relayList: (T) -> List?,
+ onDone: (T) -> Unit = {},
+ ) {
+ signer.sign(template) {
+ LocalCache.justConsume(it, null)
+ val relays = relayList(it)
+ if (relays != null) {
+ Amethyst.instance.client.sendPrivately(it, relayList = convertRelayList(relays))
+ } else {
+ Amethyst.instance.client.send(it)
+ }
+ onDone(it)
}
}
@@ -2806,28 +2823,6 @@ class Account(
}
}
- fun sendChangeChannel(template: EventTemplate) {
- if (!isWriteable()) return
-
- signer.sign(template) {
- Amethyst.instance.client.send(it)
- LocalCache.justConsume(it, null)
-
- it.channelId()?.let { LocalCache.getChannelIfExists(it)?.let { follow(it) } }
- }
- }
-
- fun sendCreateNewChannel(template: EventTemplate) {
- if (!isWriteable()) return
-
- signer.sign(template) {
- Amethyst.instance.client.send(it)
- LocalCache.justConsume(it, null)
-
- LocalCache.getChannelIfExists(it.id)?.let { follow(it) }
- }
- }
-
fun updateStatus(
oldStatus: AddressableNote,
newStatus: String,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt
index a7dd1365f..442e44fb0 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Channel.kt
@@ -33,9 +33,13 @@ import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
+import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
+import com.vitorpamplona.quartz.nip02FollowList.toImmutableListOfLists
import com.vitorpamplona.quartz.nip19Bech32.entities.NAddress
+import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.nip19Bech32.toNEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
+import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
import com.vitorpamplona.quartz.nip28PublicChat.base.ChannelData
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.utils.Hex
@@ -46,17 +50,30 @@ class PublicChatChannel(
idHex: String,
) : Channel(idHex) {
var event: ChannelCreateEvent? = null
+ var infoTags = EmptyTagList
var info = ChannelData(null, null, null, null)
override fun relays() = info.relays ?: super.relays()
+ fun toNEvent() = NEvent.create(idHex, event?.pubKey, ChannelCreateEvent.KIND, *relays().toTypedArray())
+
+ fun toNostrUri() = "nostr:${toNEvent()}"
+
fun updateChannelInfo(
creator: User,
- channelInfo: ChannelCreateEvent,
- updatedAt: Long,
+ event: ChannelCreateEvent,
) {
- this.event = channelInfo
- updateChannelInfo(creator, channelInfo.channelInfo(), updatedAt)
+ this.event = event
+ this.infoTags = event.tags.toImmutableListOfLists()
+ updateChannelInfo(creator, event.channelInfo(), event.createdAt)
+ }
+
+ fun updateChannelInfo(
+ creator: User,
+ event: ChannelMetadataEvent,
+ ) {
+ this.infoTags = event.tags.toImmutableListOfLists()
+ updateChannelInfo(creator, event.channelInfo(), event.createdAt)
}
fun updateChannelInfo(
@@ -116,9 +133,11 @@ class LiveActivitiesChannel(
.filter { it.contains(prefix, true) }
.isNotEmpty()
- fun toNAddr() = NAddress.create(address.kind, address.pubKeyHex, address.dTag, relayHintUrl())
+ fun toNAddr() = NAddress.create(address.kind, address.pubKeyHex, address.dTag, *relays().toTypedArray())
fun toATag() = ATag(address, relayHintUrl())
+
+ fun toNostrUri() = "nostr:${toNAddr()}"
}
data class Counter(
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
index 048aabbd6..dc20a6211 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
@@ -1385,7 +1385,7 @@ object LocalCache {
}
if (oldChannel.creator == null || oldChannel.creator == author) {
if (oldChannel is PublicChatChannel) {
- oldChannel.updateChannelInfo(author, event, event.createdAt)
+ oldChannel.updateChannelInfo(author, event)
}
}
}
@@ -1404,7 +1404,7 @@ object LocalCache {
val author = getOrCreateUser(event.pubKey)
if (event.createdAt > oldChannel.updatedMetadataAt) {
if (oldChannel is PublicChatChannel) {
- oldChannel.updateChannelInfo(author, event.channelInfo(), event.createdAt)
+ oldChannel.updateChannelInfo(author, event)
}
} else {
// Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt
index 984781e74..56da5a61c 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Note.kt
@@ -29,8 +29,8 @@ import com.vitorpamplona.amethyst.launchAndWaitAll
import com.vitorpamplona.amethyst.service.NostrSingleEventDataSource
import com.vitorpamplona.amethyst.service.checkNotInMainThread
import com.vitorpamplona.amethyst.service.firstFullCharOrEmoji
+import com.vitorpamplona.amethyst.service.replace
import com.vitorpamplona.amethyst.tryAndWait
-import com.vitorpamplona.amethyst.ui.actions.relays.updated
import com.vitorpamplona.amethyst.ui.note.combineWith
import com.vitorpamplona.amethyst.ui.note.toShortenHex
import com.vitorpamplona.ammolite.relays.BundledUpdate
@@ -791,28 +791,28 @@ open class Note(
// migrates these comments to a new version
replies.forEach {
note.addReply(it)
- it.replyTo = it.replyTo?.updated(this, note)
+ it.replyTo = it.replyTo?.replace(this, note)
}
reactions.forEach {
it.value.forEach {
note.addReaction(it)
- it.replyTo = it.replyTo?.updated(this, note)
+ it.replyTo = it.replyTo?.replace(this, note)
}
}
boosts.forEach {
note.addBoost(it)
- it.replyTo = it.replyTo?.updated(this, note)
+ it.replyTo = it.replyTo?.replace(this, note)
}
reports.forEach {
it.value.forEach {
note.addReport(it)
- it.replyTo = it.replyTo?.updated(this, note)
+ it.replyTo = it.replyTo?.replace(this, note)
}
}
zaps.forEach {
note.addZap(it.key, it.value)
- it.key.replyTo = it.key.replyTo?.updated(this, note)
- it.value?.replyTo = it.value?.replyTo?.updated(this, note)
+ it.key.replyTo = it.key.replyTo?.replace(this, note)
+ it.value?.replyTo = it.value?.replyTo?.replace(this, note)
}
replyTo = null
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ByteFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ByteFormatter.kt
new file mode 100644
index 000000000..d3eec1de6
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ByteFormatter.kt
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.service
+
+import kotlin.math.roundToInt
+
+fun countToHumanReadableBytes(counter: Int) =
+ when {
+ counter >= 1000000000 -> "${(counter / 1000000000f).roundToInt()} GB"
+ counter >= 1000000 -> "${(counter / 1000000f).roundToInt()} MB"
+ counter >= 1000 -> "${(counter / 1000f).roundToInt()} KB"
+ else -> "$counter"
+ }
+
+fun countToHumanReadableBytes(counter: Long) =
+ when {
+ counter >= 1000000000 -> "${(counter / 1000000000f).roundToInt()} GB"
+ counter >= 1000000 -> "${(counter / 1000000f).roundToInt()} MB"
+ counter >= 1000 -> "${(counter / 1000f).roundToInt()} KB"
+ else -> "$counter"
+ }
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/ByteFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/CountFormatter.kt
similarity index 55%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/ByteFormatter.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/service/CountFormatter.kt
index 9b3679c06..469173b69 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/ByteFormatter.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/CountFormatter.kt
@@ -18,31 +18,17 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.service
-fun countToHumanReadableBytes(counter: Int) =
- when {
- counter >= 1000000000 -> "${Math.round(counter / 1000000000f)} GB"
- counter >= 1000000 -> "${Math.round(counter / 1000000f)} MB"
- counter >= 1000 -> "${Math.round(counter / 1000f)} KB"
- else -> "$counter"
- }
-
-fun countToHumanReadableBytes(counter: Long) =
- when {
- counter >= 1000000000 -> "${Math.round(counter / 1000000000f)} GB"
- counter >= 1000000 -> "${Math.round(counter / 1000000f)} MB"
- counter >= 1000 -> "${Math.round(counter / 1000f)} KB"
- else -> "$counter"
- }
+import kotlin.math.roundToInt
fun countToHumanReadable(
counter: Int,
str: String,
) = when {
- counter >= 1000000000 -> "${Math.round(counter / 1000000000f)}G $str"
- counter >= 1000000 -> "${Math.round(counter / 1000000f)}M $str"
- counter >= 1000 -> "${Math.round(counter / 1000f)}K $str"
+ counter >= 1000000000 -> "${(counter / 1000000000f).roundToInt()}G $str"
+ counter >= 1000000 -> "${(counter / 1000000f).roundToInt()}M $str"
+ counter >= 1000 -> "${(counter / 1000f).roundToInt()}K $str"
else -> "$counter $str"
}
@@ -50,8 +36,8 @@ fun countToHumanReadable(
counter: Long,
str: String,
) = when {
- counter >= 1000000000 -> "${Math.round(counter / 1000000000f)}G $str"
- counter >= 1000000 -> "${Math.round(counter / 1000000f)}M $str"
- counter >= 1000 -> "${Math.round(counter / 1000f)}K $str"
+ counter >= 1000000000 -> "${(counter / 1000000000f).roundToInt()}G $str"
+ counter >= 1000000 -> "${(counter / 1000000f).roundToInt()}M $str"
+ counter >= 1000 -> "${(counter / 1000f).roundToInt()}K $str"
else -> "$counter $str"
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/IterableExt.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/IterableExt.kt
new file mode 100644
index 000000000..773b9c4b2
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/IterableExt.kt
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.service
+
+fun Iterable.replace(
+ old: T,
+ new: T,
+): List = map { if (it == old) new else it }
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/SetExt.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/SetExt.kt
new file mode 100644
index 000000000..03f1a5c35
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/SetExt.kt
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.service
+
+fun Set.togglePresenceInSet(item: T): Set = if (contains(item)) minus(item) else plus(item)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataScreen.kt
index 2b3fc3378..75fc40dcc 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewUserMetadataScreen.kt
@@ -221,7 +221,7 @@ fun NewUserMetadataScreen(
SelectSingleFromGallery(
isUploading = postViewModel.isUploadingImageForBanner,
tint = MaterialTheme.colorScheme.placeholderText,
- modifier = Modifier.padding(start = 5.dp),
+ modifier = Modifier.padding(start = 5.dp).align(Alignment.CenterHorizontally),
) {
postViewModel.uploadForBanner(it, context, onError = accountViewModel.toastManager::toast)
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt
index ed2b4b4ef..56191f447 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/RelaySelectionDialog.kt
@@ -47,11 +47,11 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.Nip11Retriever
-import com.vitorpamplona.amethyst.ui.actions.relays.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt
index 8e42148ee..94812d35d 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/AllMediaServersLIstView.kt
@@ -56,13 +56,13 @@ import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
-import com.vitorpamplona.amethyst.ui.actions.relays.SettingsCategory
-import com.vitorpamplona.amethyst.ui.actions.relays.SettingsCategoryWithButton
import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategoryWithButton
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.DoubleVertPadding
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt
index 021dad965..b11e1a7cc 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/uploads/SelectFromGallery.kt
@@ -24,7 +24,6 @@ import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.height
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AddPhotoAlternate
@@ -37,7 +36,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
@@ -110,22 +108,20 @@ private fun GallerySelectButton(
modifier: Modifier,
onClick: () -> Unit,
) {
- Box {
- IconButton(
- modifier = modifier.align(Alignment.Center),
- enabled = !isUploading,
- onClick = { onClick() },
- ) {
- if (!isUploading) {
- Icon(
- imageVector = Icons.Default.AddPhotoAlternate,
- contentDescription = stringRes(id = R.string.upload_image),
- modifier = Modifier.height(25.dp),
- tint = tint,
- )
- } else {
- LoadingAnimation()
- }
+ IconButton(
+ modifier = modifier,
+ enabled = !isUploading,
+ onClick = { onClick() },
+ ) {
+ if (!isUploading) {
+ Icon(
+ imageVector = Icons.Default.AddPhotoAlternate,
+ contentDescription = stringRes(id = R.string.upload_image),
+ modifier = Modifier.height(25.dp),
+ tint = tint,
+ )
+ } else {
+ LoadingAnimation()
}
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/CashuRedeem.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/CashuRedeem.kt
index 3a8303793..19387ac97 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/CashuRedeem.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/CashuRedeem.kt
@@ -67,12 +67,12 @@ import com.vitorpamplona.amethyst.ui.note.ZapIcon
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
-import com.vitorpamplona.amethyst.ui.theme.AmethystTheme
import com.vitorpamplona.amethyst.ui.theme.CashuCardBorders
import com.vitorpamplona.amethyst.ui.theme.Size18Modifier
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.SmallishBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
+import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
@@ -129,14 +129,12 @@ fun CashuPreviewPreview() {
sharedPreferencesViewModel.init()
sharedPreferencesViewModel.updateTheme(ThemeType.DARK)
- AmethystTheme(sharedPrefsViewModel = sharedPreferencesViewModel) {
- Column {
- CashuPreviewNew(
- token = CashuToken("token", "mint", 32400, listOf()),
- melt = { token, context, onDone -> },
- toast = { title, message -> },
- )
- }
+ ThemeComparisonColumn {
+ CashuPreviewNew(
+ token = CashuToken("token", "mint", 32400, listOf()),
+ melt = { token, context, onDone -> },
+ toast = { title, message -> },
+ )
}
}
@@ -210,7 +208,6 @@ fun CashuPreviewNew(
Text(
"Redeem",
- color = MaterialTheme.colorScheme.onBackground,
fontSize = 16.sp,
)
}
@@ -232,7 +229,7 @@ fun CashuPreviewNew(
shape = SmallishBorder,
contentPadding = PaddingValues(0.dp),
) {
- OpenInNewIcon(Size18Modifier, tint = MaterialTheme.colorScheme.onBackground)
+ OpenInNewIcon(Size18Modifier)
}
Spacer(modifier = StdHorzSpacer)
@@ -245,7 +242,7 @@ fun CashuPreviewNew(
shape = SmallishBorder,
contentPadding = PaddingValues(0.dp),
) {
- CopyIcon(Size18Modifier, tint = MaterialTheme.colorScheme.onBackground)
+ CopyIcon(Size18Modifier)
}
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt
index ac331bde1..1eb8b4454 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/ExpandableRichTextViewer.kt
@@ -26,9 +26,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
@@ -50,7 +48,6 @@ import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.ButtonPadding
import com.vitorpamplona.amethyst.ui.theme.StdTopPadding
-import com.vitorpamplona.amethyst.ui.theme.secondaryButtonBackground
import com.vitorpamplona.quartz.nip02FollowList.ImmutableListOfLists
object ShowFullTextCache {
@@ -128,16 +125,12 @@ fun ExpandableRichTextViewer(
@Composable
fun ShowMoreButton(onClick: () -> Unit) {
- Button(
+ FilledTonalButton(
modifier = StdTopPadding,
onClick = onClick,
shape = ButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.secondaryButtonBackground,
- ),
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(R.string.show_more), color = Color.White)
+ Text(text = stringRes(R.string.show_more))
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/SensitivityWarning.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/SensitivityWarning.kt
index 356c9ec3f..b73e56957 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/SensitivityWarning.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/components/SensitivityWarning.kt
@@ -32,8 +32,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.rounded.Warning
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -161,19 +160,14 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
}
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
- Button(
+ FilledTonalButton(
modifier = Modifier.padding(top = 10.dp),
onClick = onDismiss,
shape = ButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary,
- ),
contentPadding = ButtonPadding,
) {
Text(
text = stringRes(R.string.show_anyway),
- color = Color.White,
)
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListKnownFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListKnownFeedFilter.kt
index 58c139aae..0d9622e37 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListKnownFeedFilter.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListKnownFeedFilter.kt
@@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.ui.dal
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
-import com.vitorpamplona.amethyst.ui.actions.relays.updated
+import com.vitorpamplona.amethyst.service.replace
import com.vitorpamplona.quartz.nip01Core.tags.events.taggedEventIds
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
@@ -90,7 +90,7 @@ class ChatroomListKnownFeedFilter(
if (newNotePair.key == oldNote.channelHex()) {
hasUpdated = true
if ((newNotePair.value.createdAt() ?: 0) > (oldNote.createdAt() ?: 0)) {
- myNewList = myNewList.updated(oldNote, newNotePair.value)
+ myNewList = myNewList.replace(oldNote, newNotePair.value)
}
}
}
@@ -107,7 +107,7 @@ class ChatroomListKnownFeedFilter(
if (newNotePair.key == oldRoom) {
hasUpdated = true
if ((newNotePair.value.createdAt() ?: 0) > (oldNote.createdAt() ?: 0)) {
- myNewList = myNewList.updated(oldNote, newNotePair.value)
+ myNewList = myNewList.replace(oldNote, newNotePair.value)
}
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListNewFeedFilter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListNewFeedFilter.kt
index faee792da..1746610ae 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListNewFeedFilter.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListNewFeedFilter.kt
@@ -22,7 +22,7 @@ package com.vitorpamplona.amethyst.ui.dal
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.Note
-import com.vitorpamplona.amethyst.ui.actions.relays.updated
+import com.vitorpamplona.amethyst.service.replace
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKey
import com.vitorpamplona.quartz.nip17Dm.base.ChatroomKeyable
@@ -77,7 +77,7 @@ class ChatroomListNewFeedFilter(
if (newNotePair.key == oldRoom) {
hasUpdated = true
if ((newNotePair.value.createdAt() ?: 0) > (oldNote.createdAt() ?: 0)) {
- myNewList = myNewList.updated(oldNote, newNotePair.value)
+ myNewList = myNewList.replace(oldNote, newNotePair.value)
}
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt
index bc76d11a3..6a5980cca 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt
@@ -47,7 +47,6 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataScreen
-import com.vitorpamplona.amethyst.ui.actions.relays.AllRelayListView
import com.vitorpamplona.amethyst.ui.components.DisplayNotifyMessages
import com.vitorpamplona.amethyst.ui.components.getActivity
import com.vitorpamplona.amethyst.ui.components.toasts.DisplayErrorMessages
@@ -61,6 +60,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarks.BookmarkListScree
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.ChatroomByAuthorScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.ChatroomScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.ChannelScreen
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.metadata.ChannelMetadataScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.MessagesScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.communities.CommunityScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.DiscoverScreen
@@ -72,6 +72,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.HomeScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.ListsScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.AllRelayListScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.search.SearchScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.NIP47SetupScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.SecurityFiltersScreen
@@ -269,6 +270,22 @@ fun AppNavigation(
)
}
+ composable(
+ Route.ChannelMetadataEdit.route,
+ Route.ChannelMetadataEdit.arguments,
+ enterTransition = { slideInVerticallyFromBottom },
+ exitTransition = { scaleOut },
+ popEnterTransition = { scaleIn },
+ popExitTransition = { slideOutVerticallyToBottom },
+ content = {
+ ChannelMetadataScreen(
+ channelId = it.id(),
+ accountViewModel = accountViewModel,
+ nav = nav,
+ )
+ },
+ )
+
composable(
Route.Event.route,
Route.Event.arguments,
@@ -313,7 +330,7 @@ fun AppNavigation(
content = {
val relayToAdd = it.arguments?.getString("toAdd")
- AllRelayListView(
+ AllRelayListScreen(
relayToAdd = relayToAdd,
accountViewModel = accountViewModel,
nav = nav,
@@ -329,7 +346,7 @@ fun AppNavigation(
popEnterTransition = { scaleIn },
popExitTransition = { slideOutVerticallyToBottom },
) {
- val draftMessage = it.arguments?.getString("message")?.ifBlank { null }
+ val draftMessage = it.message()?.ifBlank { null }
val attachment =
it.arguments?.getString("attachment")?.ifBlank { null }?.let {
Uri.parse(it)
@@ -339,8 +356,8 @@ fun AppNavigation(
val fork = it.arguments?.getString("fork")
val version = it.arguments?.getString("version")
val draft = it.arguments?.getString("draft")
- val enableMessageInterface = it.arguments?.getBoolean("enableMessageInterface") ?: false
- val enableGeolocation = it.arguments?.getBoolean("enableGeolocation") ?: false
+ val enableMessageInterface = it.arguments?.getBoolean("enableMessageInterface") == true
+ val enableGeolocation = it.arguments?.getBoolean("enableGeolocation") == true
NewPostScreen(
message = draftMessage,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt
index 56c1733b7..c7e9a269f 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt
@@ -213,6 +213,20 @@ sealed class Route(
arguments = listOf(navArgument("id") { type = NavType.StringType }).toImmutableList(),
)
+ object ChannelMetadataEdit :
+ Route(
+ route = "ChannelMetadataEdit?id={id}",
+ icon = R.drawable.ic_moments,
+ arguments =
+ listOf(
+ navArgument("id") {
+ type = NavType.StringType
+ nullable = true
+ defaultValue = null
+ },
+ ).toImmutableList(),
+ )
+
object Event :
Route(
route = "Event/{id}",
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/TopBarExtensibleWithBackButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/TopBarExtensibleWithBackButton.kt
index 29d165f84..23883c6cc 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/TopBarExtensibleWithBackButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/TopBarExtensibleWithBackButton.kt
@@ -21,7 +21,6 @@
package com.vitorpamplona.amethyst.ui.navigation
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
@@ -30,6 +29,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.width
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
@@ -38,6 +39,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
+import com.vitorpamplona.amethyst.ui.theme.isLight
@Composable
fun TopBarExtensibleWithBackButton(
@@ -90,11 +92,8 @@ fun MyExtensibleTopAppBar(
)
if (expanded.value && extendableRow != null) {
- Row(
- Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.Start,
- verticalAlignment = Alignment.CenterVertically,
- ) {
+ val elevation = if (MaterialTheme.colorScheme.isLight) 1.dp else 10.dp
+ Surface(modifier = Modifier.fillMaxWidth(), tonalElevation = elevation) {
Column { extendableRow() }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/BlankNote.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/BlankNote.kt
index d656dd506..b98804779 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/BlankNote.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/BlankNote.kt
@@ -26,9 +26,7 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -155,17 +153,13 @@ fun HiddenNote(
}
}
- Button(
+ FilledTonalButton(
modifier = Modifier.padding(top = 10.dp),
onClick = onClick,
shape = ButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- contentColor = MaterialTheme.colorScheme.primary,
- ),
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(R.string.show_anyway), color = Color.White)
+ Text(text = stringRes(R.string.show_anyway))
}
}
}
@@ -201,17 +195,13 @@ fun HiddenNoteByMe(
textAlign = TextAlign.Center,
)
- Button(
+ FilledTonalButton(
modifier = Modifier.padding(top = 10.dp),
onClick = onClick,
shape = ButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- contentColor = MaterialTheme.colorScheme.primary,
- ),
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(R.string.show_anyway), color = Color.White)
+ Text(text = stringRes(R.string.show_anyway))
}
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/Icons.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/Icons.kt
index b9120f386..79d2bbb95 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/Icons.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/Icons.kt
@@ -312,27 +312,19 @@ fun CashuIcon(modifier: Modifier) {
}
@Composable
-fun CopyIcon(
- modifier: Modifier,
- tint: Color = Color.Unspecified,
-) {
+fun CopyIcon(modifier: Modifier) {
Icon(
imageVector = Icons.Default.ContentCopy,
stringRes(id = R.string.copy_to_clipboard),
- tint = tint,
modifier = modifier,
)
}
@Composable
-fun OpenInNewIcon(
- modifier: Modifier,
- tint: Color = Color.Unspecified,
-) {
+fun OpenInNewIcon(modifier: Modifier) {
Icon(
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
stringRes(id = R.string.copy_to_clipboard),
- tint = tint,
modifier = modifier,
)
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt
index 38d0257c8..c92499b0a 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/NoteCompose.kt
@@ -87,7 +87,6 @@ import com.vitorpamplona.amethyst.ui.note.types.EditState
import com.vitorpamplona.amethyst.ui.note.types.EmptyState
import com.vitorpamplona.amethyst.ui.note.types.FileHeaderDisplay
import com.vitorpamplona.amethyst.ui.note.types.FileStorageHeaderDisplay
-import com.vitorpamplona.amethyst.ui.note.types.JustVideoDisplay
import com.vitorpamplona.amethyst.ui.note.types.PictureDisplay
import com.vitorpamplona.amethyst.ui.note.types.RenderAppDefinition
import com.vitorpamplona.amethyst.ui.note.types.RenderAudioHeader
@@ -194,7 +193,6 @@ import com.vitorpamplona.quartz.nip58Badges.BadgeAwardEvent
import com.vitorpamplona.quartz.nip58Badges.BadgeDefinitionEvent
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
-import com.vitorpamplona.quartz.nip71Video.VideoEvent
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent
import com.vitorpamplona.quartz.nip71Video.VideoVerticalEvent
import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent
@@ -328,9 +326,6 @@ fun AcceptableNote(
)
}
is BadgeDefinitionEvent -> BadgeDisplay(baseNote = baseNote)
- is FileHeaderEvent -> FileHeaderDisplay(baseNote, false, ContentScale.FillWidth, accountViewModel)
- is FileStorageHeaderEvent -> FileStorageHeaderDisplay(baseNote, false, ContentScale.FillWidth, accountViewModel)
- is VideoEvent -> JustVideoDisplay(baseNote, false, ContentScale.FillWidth, accountViewModel)
else ->
LongPressToQuickAction(baseNote = baseNote, accountViewModel = accountViewModel) { showPopup ->
CheckNewAndRenderNote(
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayCompose.kt
index b726e1a93..10db8440c 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayCompose.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayCompose.kt
@@ -25,9 +25,8 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
@@ -35,7 +34,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -121,33 +119,25 @@ private fun RelayOptions(
@Composable
fun AddRelayButton(onClick: () -> Unit) {
- Button(
+ OutlinedButton(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onClick,
shape = ButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary,
- ),
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(id = R.string.add), color = Color.White)
+ Text(text = stringRes(id = R.string.add))
}
}
@Composable
fun RemoveRelayButton(onClick: () -> Unit) {
- Button(
+ OutlinedButton(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onClick,
shape = ButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary,
- ),
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(R.string.remove), color = Color.White)
+ Text(text = stringRes(R.string.remove))
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt
index d96d742a1..9b2e44df5 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/RelayListRow.kt
@@ -54,11 +54,11 @@ import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
import com.vitorpamplona.amethyst.service.Nip11Retriever
-import com.vitorpamplona.amethyst.ui.actions.relays.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.components.ClickableBox
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt
index a416c60bb..fd946c474 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/UserCompose.kt
@@ -52,7 +52,9 @@ fun UserCompose(
UserPicture(baseUser, Size55dp, accountViewModel = accountViewModel, nav = nav)
Column(modifier = remember { Modifier.padding(start = 10.dp).weight(1f) }) {
- Row(verticalAlignment = Alignment.CenterVertically) { UsernameDisplay(baseUser, accountViewModel = accountViewModel) }
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ UsernameDisplay(baseUser, accountViewModel = accountViewModel)
+ }
AboutDisplay(baseUser)
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt
index 1a9f38ec0..5a04c5e0b 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt
@@ -34,7 +34,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -165,8 +164,6 @@ fun UserActionOptions(
baseAuthor: User,
accountViewModel: AccountViewModel,
) {
- val scope = rememberCoroutineScope()
-
WatchIsHiddenUser(baseAuthor, accountViewModel) { isHidden ->
if (isHidden) {
ShowUserButton { accountViewModel.show(baseAuthor) }
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddInboxRelayForDMCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddInboxRelayForDMCard.kt
index 8132ccda0..d81c829d5 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddInboxRelayForDMCard.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddInboxRelayForDMCard.kt
@@ -39,12 +39,12 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R
-import com.vitorpamplona.amethyst.ui.actions.relays.AddDMRelayListDialog
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.AddDMRelayListDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.BigPadding
import com.vitorpamplona.amethyst.ui.theme.StdPadding
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddInboxRelayForSearchCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddInboxRelayForSearchCard.kt
index 83cfc46ff..feb746b93 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddInboxRelayForSearchCard.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddInboxRelayForSearchCard.kt
@@ -39,12 +39,12 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R
-import com.vitorpamplona.amethyst.ui.actions.relays.AddSearchRelayListDialog
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.AddSearchRelayListDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.BigPadding
import com.vitorpamplona.amethyst.ui.theme.StdPadding
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddRemoveButtons.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddRemoveButtons.kt
index d996c57f6..cd1d15aae 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddRemoveButtons.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/AddRemoveButtons.kt
@@ -24,11 +24,10 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
+import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -62,7 +61,7 @@ fun AddButton(
modifier: Modifier = Modifier.padding(start = 3.dp),
onClick: () -> Unit,
) {
- Button(
+ OutlinedButton(
modifier = modifier,
onClick = {
if (isActive) {
@@ -73,7 +72,7 @@ fun AddButton(
enabled = isActive,
contentPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp),
) {
- Text(text = stringRes(text), color = Color.White, textAlign = TextAlign.Center)
+ Text(text = stringRes(text), textAlign = TextAlign.Center)
}
}
@@ -82,7 +81,7 @@ fun RemoveButton(
isActive: Boolean = true,
onClick: () -> Unit,
) {
- Button(
+ OutlinedButton(
modifier = Modifier.padding(start = 3.dp),
onClick = {
if (isActive) {
@@ -93,6 +92,6 @@ fun RemoveButton(
enabled = isActive,
contentPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp),
) {
- Text(text = stringRes(R.string.remove), color = Color.White)
+ Text(text = stringRes(R.string.remove))
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Torrent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Torrent.kt
index 3e5675030..e8a2ee877 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Torrent.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/Torrent.kt
@@ -52,7 +52,7 @@ import androidx.core.content.ContextCompat
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
-import com.vitorpamplona.amethyst.ui.actions.relays.countToHumanReadableBytes
+import com.vitorpamplona.amethyst.service.countToHumanReadableBytes
import com.vitorpamplona.amethyst.ui.components.ShowMoreButton
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.INav
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TorrentComment.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TorrentComment.kt
index e7444f8bf..3fd0f6a86 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TorrentComment.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/types/TorrentComment.kt
@@ -47,7 +47,7 @@ import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
-import com.vitorpamplona.amethyst.ui.actions.relays.countToHumanReadableBytes
+import com.vitorpamplona.amethyst.service.countToHumanReadableBytes
import com.vitorpamplona.amethyst.ui.components.GenericLoadable
import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt
index dcc68181f..7ec813330 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/NewPostScreen.kt
@@ -1698,19 +1698,11 @@ fun CreateButton(
modifier: Modifier = Modifier,
) {
Button(
+ enabled = isActive,
modifier = modifier,
- onClick = {
- if (isActive) {
- onPost()
- }
- },
- shape = ButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = if (isActive) MaterialTheme.colorScheme.primary else Color.Gray,
- ),
+ onClick = onPost,
) {
- Text(text = stringRes(R.string.create), color = Color.White)
+ Text(text = stringRes(R.string.create))
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderChangeChannelMetadataNote.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderChangeChannelMetadataNote.kt
index 29aa52549..fa786c9cd 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderChangeChannelMetadataNote.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderChangeChannelMetadataNote.kt
@@ -22,15 +22,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
-import androidx.compose.ui.Modifier
+import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
-import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
-import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
-import com.vitorpamplona.amethyst.ui.stringRes
-import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
+import com.vitorpamplona.quartz.nip02FollowList.toImmutableListOfLists
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
@Composable
@@ -41,27 +38,19 @@ fun RenderChangeChannelMetadataNote(
nav: INav,
) {
val noteEvent = note.event as? ChannelMetadataEvent ?: return
+ val channelInfo = remember(noteEvent) { noteEvent.channelInfo() }
+ val tags =
+ remember(noteEvent) {
+ noteEvent.tags.toImmutableListOfLists()
+ }
- val channelInfo = noteEvent.channelInfo()
- val text =
- note.author?.toBestDisplayName().toString() +
- " ${stringRes(R.string.changed_chat_name_to)} '" +
- (channelInfo.name ?: "") +
- "', ${stringRes(R.string.description_to)} '" +
- (channelInfo.about ?: "") +
- "' ${stringRes(R.string.and_picture_to)} " +
- (channelInfo.picture ?: "")
-
- TranslatableRichTextViewer(
- content = text,
- canPreview = true,
- quotesLeft = 0,
- modifier = Modifier,
- tags = note.author?.info?.tags ?: EmptyTagList,
- backgroundColor = bgColor,
- id = note.idHex,
- callbackUri = note.toNostrUri(),
- accountViewModel = accountViewModel,
- nav = nav,
+ RenderChannelData(
+ noteEvent.id,
+ note.toNostrUri(),
+ channelInfo,
+ tags,
+ bgColor,
+ accountViewModel,
+ nav,
)
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderCreateChannelNote.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderCreateChannelNote.kt
index ac01d26ac..4cd262587 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderCreateChannelNote.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/feed/types/RenderCreateChannelNote.kt
@@ -20,19 +20,63 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.Note
+import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
+import com.vitorpamplona.amethyst.service.Nip11Retriever
+import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
+import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
+import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.stringRes
+import com.vitorpamplona.amethyst.ui.theme.RelayIconFilter
+import com.vitorpamplona.amethyst.ui.theme.Size20dp
+import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
+import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
+import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
+import com.vitorpamplona.amethyst.ui.theme.largeProfilePictureModifier
+import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
+import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
+import com.vitorpamplona.quartz.nip02FollowList.ImmutableListOfLists
+import com.vitorpamplona.quartz.nip02FollowList.toImmutableListOfLists
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
+import com.vitorpamplona.quartz.nip28PublicChat.base.ChannelData
+import com.vitorpamplona.quartz.nip65RelayList.RelayUrlFormatter
@Composable
fun RenderCreateChannelNote(
@@ -42,27 +86,272 @@ fun RenderCreateChannelNote(
nav: INav,
) {
val noteEvent = note.event as? ChannelCreateEvent ?: return
- val channelInfo = remember { noteEvent.channelInfo() }
+ val channelInfo = remember(noteEvent) { noteEvent.channelInfo() }
+ val tags =
+ remember(noteEvent) {
+ noteEvent.tags.toImmutableListOfLists()
+ }
- val text =
- note.author?.toBestDisplayName().toString() +
- " ${stringRes(R.string.created)} " +
- (channelInfo.name ?: "") +
- " ${stringRes(R.string.with_description_of)} '" +
- (channelInfo.about ?: "") +
- "' ${stringRes(R.string.and_picture_to)} " +
- (channelInfo.picture ?: "")
-
- TranslatableRichTextViewer(
- content = text,
- canPreview = true,
- quotesLeft = 0,
- modifier = Modifier,
- tags = note.author?.info?.tags ?: EmptyTagList,
- backgroundColor = bgColor,
- id = note.idHex,
- callbackUri = note.toNostrUri(),
- accountViewModel = accountViewModel,
- nav = nav,
+ RenderChannelData(
+ noteEvent.id,
+ note.toNostrUri(),
+ channelInfo,
+ tags,
+ bgColor,
+ accountViewModel,
+ nav,
)
}
+
+@Preview
+@Composable
+fun RenderChannelDataPreview() {
+ ThemeComparisonRow {
+ RenderChannelData(
+ id = "bbaacc",
+ uri = "nostr:nevent1...",
+ channelInfo =
+ ChannelData(
+ "My Group",
+ "Testing About me",
+ "http://test.com",
+ listOf("wss://nostr.mom", "wss://nos.lol"),
+ ),
+ tags = EmptyTagList,
+ bgColor = remember { mutableStateOf(Color.Transparent) },
+ accountViewModel = mockAccountViewModel(),
+ nav = EmptyNav,
+ )
+ }
+}
+
+@Composable
+fun RenderChannelData(
+ id: HexKey,
+ uri: String,
+ channelInfo: ChannelData,
+ tags: ImmutableListOfLists,
+ bgColor: MutableState,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ Column {
+ Row {
+ TranslatableRichTextViewer(
+ content = stringRes(R.string.changed_chat_profile_to),
+ canPreview = true,
+ quotesLeft = 1,
+ modifier = Modifier,
+ tags = tags,
+ backgroundColor = bgColor,
+ id = id,
+ callbackUri = uri,
+ accountViewModel = accountViewModel,
+ nav = nav,
+ )
+ }
+
+ channelInfo.picture?.let {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
+ ) {
+ RobohashFallbackAsyncImage(
+ robot = id,
+ model = it,
+ contentDescription = stringRes(R.string.channel_image),
+ modifier = MaterialTheme.colorScheme.largeProfilePictureModifier,
+ loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
+ loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
+ )
+ }
+ }
+
+ channelInfo.name?.let {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
+ ) {
+ CreateTextWithEmoji(
+ text = it,
+ tags = tags,
+ fontWeight = FontWeight.Bold,
+ fontSize = 20.sp,
+ )
+ }
+ }
+
+ channelInfo.about?.let {
+ Row(
+ modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
+ ) {
+ TranslatableRichTextViewer(
+ content = it,
+ canPreview = true,
+ quotesLeft = 1,
+ modifier = Modifier,
+ tags = tags,
+ backgroundColor = bgColor,
+ id = id,
+ callbackUri = uri,
+ accountViewModel = accountViewModel,
+ nav = nav,
+ )
+ }
+ }
+
+ channelInfo.relays?.let {
+ Text(
+ stringRes(R.string.public_chat_relays_title) + ": ",
+ modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
+ )
+ it.forEach {
+ Spacer(StdVertSpacer)
+ RenderRelayLinePublicChat(
+ it,
+ accountViewModel,
+ nav,
+ )
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun RenderRelayLinePreview() {
+ ThemeComparisonRow {
+ RenderRelayLine(
+ "wss://nos.lol",
+ "http://icon.com/icon.ico",
+ Modifier,
+ true,
+ true,
+ )
+ }
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun RenderRelayLinePublicChat(
+ dirtyUrl: String,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ @Suppress("ProduceStateDoesNotAssignValue")
+ val relayInfo by produceState(
+ initialValue = Nip11CachedRetriever.getFromCache(dirtyUrl),
+ ) {
+ if (value == null) {
+ accountViewModel.retrieveRelayDocument(
+ dirtyUrl,
+ onInfo = {
+ value = it
+ },
+ onError = { url, errorCode, exceptionMessage ->
+ },
+ )
+ }
+ }
+
+ var openRelayDialog by remember { mutableStateOf(false) }
+
+ val info =
+ remember(dirtyUrl) {
+ RelayBriefInfoCache.get(RelayUrlFormatter.normalize(dirtyUrl))
+ }
+
+ if (openRelayDialog && relayInfo != null) {
+ RelayInformationDialog(
+ onClose = { openRelayDialog = false },
+ relayInfo = relayInfo!!,
+ relayBriefInfo = info,
+ accountViewModel = accountViewModel,
+ nav = nav,
+ )
+ }
+
+ val clipboardManager = LocalClipboardManager.current
+ val clickableModifier =
+ remember(dirtyUrl) {
+ Modifier.combinedClickable(
+ onLongClick = {
+ clipboardManager.setText(AnnotatedString(dirtyUrl))
+ },
+ onClick = {
+ accountViewModel.retrieveRelayDocument(
+ dirtyUrl,
+ onInfo = {
+ openRelayDialog = true
+ },
+ onError = { url, errorCode, exceptionMessage ->
+ accountViewModel.toastManager.toast(
+ R.string.unable_to_download_relay_document,
+ when (errorCode) {
+ Nip11Retriever.ErrorCode.FAIL_TO_ASSEMBLE_URL ->
+ R.string.relay_information_document_error_failed_to_assemble_url
+
+ Nip11Retriever.ErrorCode.FAIL_TO_REACH_SERVER ->
+ R.string.relay_information_document_error_failed_to_reach_server
+
+ Nip11Retriever.ErrorCode.FAIL_TO_PARSE_RESULT ->
+ R.string.relay_information_document_error_failed_to_parse_response
+
+ Nip11Retriever.ErrorCode.FAIL_WITH_HTTP_STATUS ->
+ R.string.relay_information_document_error_failed_with_http
+ },
+ url,
+ exceptionMessage ?: errorCode.toString(),
+ )
+ },
+ )
+ },
+ )
+ }
+
+ RenderRelayLine(
+ info.displayUrl,
+ relayInfo?.icon ?: info.favIcon,
+ clickableModifier,
+ showPicture = accountViewModel.settings.showProfilePictures.value,
+ loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
+ )
+}
+
+@Composable
+fun RenderRelayLine(
+ url: String,
+ icon: String?,
+ modifier: Modifier = Modifier,
+ showPicture: Boolean = true,
+ loadRobohash: Boolean = true,
+) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center,
+ modifier = modifier,
+ ) {
+ Text(" -")
+
+ Spacer(modifier = StdHorzSpacer)
+
+ RobohashFallbackAsyncImage(
+ robot = url,
+ model = icon,
+ contentDescription = stringRes(id = R.string.relay_info, url),
+ colorFilter = RelayIconFilter,
+ modifier =
+ Modifier
+ .size(Size20dp)
+ .clip(shape = CircleShape),
+ loadProfilePicture = showPicture,
+ loadRobohash = loadRobohash,
+ )
+
+ Spacer(modifier = StdHorzSpacer)
+
+ Text(
+ text = url,
+ )
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/RenderRoomTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/RenderRoomTopBar.kt
index 2df854f7b..c6236e5ee 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/RenderRoomTopBar.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/header/RenderRoomTopBar.kt
@@ -31,7 +31,7 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.EditNote
-import androidx.compose.material3.Button
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
@@ -42,7 +42,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@@ -132,7 +131,7 @@ fun GroupMembersHeader(
val list = remember(room) { room.users.toPersistentList() }
Row(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier.fillMaxWidth().padding(5.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
@@ -181,7 +180,7 @@ private fun EditRoomSubjectButton(
NewChatroomSubjectDialog({ wantsToPost = false }, accountViewModel, room)
}
- Button(
+ FilledTonalButton(
modifier =
Modifier
.padding(horizontal = 3.dp)
@@ -190,7 +189,6 @@ private fun EditRoomSubjectButton(
contentPadding = ZeroPadding,
) {
Icon(
- tint = Color.White,
imageVector = Icons.Default.EditNote,
contentDescription = stringRes(R.string.edits_the_channel_metadata),
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt
index 415208e92..1a85cae81 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt
@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -62,11 +63,9 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.utils.ThinSendButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.EditFieldBorder
-import com.vitorpamplona.amethyst.ui.theme.EditFieldLeadingIconModifier
import com.vitorpamplona.amethyst.ui.theme.EditFieldModifier
import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier
-import com.vitorpamplona.amethyst.ui.theme.Size30Modifier
-import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonRow
+import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
@@ -76,12 +75,12 @@ import kotlinx.coroutines.launch
@Preview
@Composable
-fun PrivateMessageEditFieldRow() {
+fun PrivateMessageEditFieldRowPreview() {
val channelScreenModel: ChatNewMessageViewModel = viewModel()
val accountViewModel = mockAccountViewModel()
channelScreenModel.init(accountViewModel)
- ThemeComparisonRow {
+ ThemeComparisonColumn {
PrivateMessageEditFieldRow(
channelScreenModel = channelScreenModel,
accountViewModel = accountViewModel,
@@ -170,12 +169,12 @@ fun PrivateMessageEditFieldRow(
leadingIcon = {
Row(
verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.padding(horizontal = 6.dp),
+ modifier = Modifier.padding(start = 4.dp, end = 10.dp),
) {
SelectFromGallery(
isUploading = channelScreenModel.isUploadingImage,
tint = MaterialTheme.colorScheme.placeholderText,
- modifier = EditFieldLeadingIconModifier,
+ modifier = Modifier,
onImageChosen = channelScreenModel::pickedMedia,
)
@@ -190,7 +189,7 @@ fun PrivateMessageEditFieldRow(
}
IconButton(
- modifier = Size30Modifier,
+ modifier = Modifier.width(30.dp),
onClick = {
if (
!accountViewModel.account.settings.hideNIP17WarningDialog &&
@@ -208,7 +207,7 @@ fun PrivateMessageEditFieldRow(
modifier =
Modifier
.padding(top = 2.dp)
- .size(18.dp),
+ .size(20.dp),
tint = MaterialTheme.colorScheme.primary,
)
} else {
@@ -216,7 +215,7 @@ fun PrivateMessageEditFieldRow(
modifier =
Modifier
.padding(top = 2.dp)
- .size(18.dp),
+ .size(20.dp),
tint = MaterialTheme.colorScheme.placeholderText,
)
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongChannelActionOptions.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongChannelActionOptions.kt
deleted file mode 100644
index 84a146658..000000000
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongChannelActionOptions.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-/**
- * Copyright (c) 2024 Vitor Pamplona
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
- * Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header
-
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.livedata.observeAsState
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
-import androidx.lifecycle.distinctUntilChanged
-import androidx.lifecycle.map
-import com.vitorpamplona.amethyst.model.PublicChatChannel
-import com.vitorpamplona.amethyst.ui.components.LoadNote
-import com.vitorpamplona.amethyst.ui.navigation.INav
-import com.vitorpamplona.amethyst.ui.note.LikeReaction
-import com.vitorpamplona.amethyst.ui.note.ZapReaction
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.EditButton
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.JoinChatButton
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.LeaveChatButton
-import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
-import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
-import com.vitorpamplona.quartz.nip01Core.tags.events.isTaggedEvent
-
-@Composable
-fun ShortChannelActionOptions(
- channel: PublicChatChannel,
- accountViewModel: AccountViewModel,
- nav: INav,
-) {
- LoadNote(baseNoteHex = channel.idHex, accountViewModel) {
- it?.let {
- Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = RowColSpacing) {
- LikeReaction(
- baseNote = it,
- grayTint = MaterialTheme.colorScheme.onSurface,
- accountViewModel = accountViewModel,
- nav,
- )
- ZapReaction(
- baseNote = it,
- grayTint = MaterialTheme.colorScheme.onSurface,
- accountViewModel = accountViewModel,
- nav = nav,
- )
- Spacer(modifier = StdHorzSpacer)
- }
- }
- }
-
- WatchChannelFollows(channel, accountViewModel) { isFollowing ->
- if (!isFollowing) {
- JoinChatButton(channel, accountViewModel, nav)
- }
- }
-}
-
-@Composable
-fun LongChannelActionOptions(
- channel: PublicChatChannel,
- accountViewModel: AccountViewModel,
- nav: INav,
-) {
- val isMe by
- remember(accountViewModel) {
- derivedStateOf { channel.creator == accountViewModel.account.userProfile() }
- }
-
- if (isMe) {
- EditButton(channel, accountViewModel)
- }
-
- WatchChannelFollows(channel, accountViewModel) { isFollowing ->
- if (isFollowing) {
- LeaveChatButton(channel, accountViewModel, nav)
- }
- }
-}
-
-@Composable
-private fun WatchChannelFollows(
- channel: PublicChatChannel,
- accountViewModel: AccountViewModel,
- content: @Composable (Boolean) -> Unit,
-) {
- val isFollowing by
- accountViewModel
- .userProfile()
- .live()
- .follows
- .map { it.user.latestContactList?.isTaggedEvent(channel.idHex) ?: false }
- .distinctUntilChanged()
- .observeAsState(
- accountViewModel.userProfile().latestContactList?.isTaggedEvent(channel.idHex) ?: false,
- )
-
- content(isFollowing)
-}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt
index 1bd744c6d..eed1fe260 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/LongPublicChatChannelHeader.kt
@@ -20,25 +20,35 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.distinctUntilChanged
+import androidx.lifecycle.map
import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.LoadNote
+import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
@@ -46,21 +56,64 @@ import com.vitorpamplona.amethyst.ui.note.NoteUsernameDisplay
import com.vitorpamplona.amethyst.ui.note.elements.MoreOptionsButton
import com.vitorpamplona.amethyst.ui.note.elements.NormalTimeAgo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.EditButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.LeaveChatButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.LinkChatButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.OpenChatButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.ShareChatButton
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.Size25dp
-import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
+import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
+import com.vitorpamplona.amethyst.ui.theme.largeProfilePictureModifier
+import com.vitorpamplona.quartz.nip01Core.tags.events.isTaggedEvent
@Composable
fun LongPublicChatChannelHeader(
baseChannel: PublicChatChannel,
- lineModifier: Modifier = Modifier.padding(horizontal = 10.dp, vertical = 5.dp),
+ lineModifier: Modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp, vertical = 5.dp),
accountViewModel: AccountViewModel,
nav: INav,
) {
val channelState by baseChannel.live.observeAsState()
val channel = channelState?.channel as? PublicChatChannel ?: return
+ Spacer(StdVertSpacer)
+
+ channel.info.picture?.let {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = lineModifier,
+ ) {
+ RobohashFallbackAsyncImage(
+ robot = baseChannel.idHex,
+ model = it,
+ contentDescription = stringRes(R.string.channel_image),
+ modifier = MaterialTheme.colorScheme.largeProfilePictureModifier,
+ loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
+ loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
+ )
+ }
+ }
+
+ channel.info.name?.let {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = lineModifier,
+ ) {
+ CreateTextWithEmoji(
+ text = it,
+ tags = channel.infoTags,
+ fontWeight = FontWeight.Bold,
+ fontSize = 20.sp,
+ )
+ }
+ }
+
+ Row(horizontalArrangement = Arrangement.Center, modifier = lineModifier) {
+ LongChannelActionOptions(channel, accountViewModel, nav)
+ }
+
Row(lineModifier) {
val summary = remember(channelState) { channel.summary()?.ifBlank { null } }
@@ -73,7 +126,7 @@ fun LongPublicChatChannelHeader(
content = summary ?: stringRes(id = R.string.groups_no_descriptor),
canPreview = false,
quotesLeft = 1,
- tags = EmptyTagList,
+ tags = channel.infoTags,
backgroundColor = background,
id = baseChannel.idHex,
accountViewModel = accountViewModel,
@@ -81,9 +134,6 @@ fun LongPublicChatChannelHeader(
)
}
}
-
- Spacer(DoubleHorzSpacer)
- LongChannelActionOptions(channel, accountViewModel, nav)
}
LoadNote(baseNoteHex = channel.idHex, accountViewModel) { loadingNote ->
@@ -120,4 +170,54 @@ fun LongPublicChatChannelHeader(
}
}
}
+
+ Spacer(StdVertSpacer)
+}
+
+@Composable
+fun LongChannelActionOptions(
+ channel: PublicChatChannel,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ val isMe by
+ remember(accountViewModel) {
+ derivedStateOf { channel.creator == accountViewModel.account.userProfile() }
+ }
+
+ OpenChatButton(channel, accountViewModel, nav)
+
+ LinkChatButton(channel, accountViewModel, nav)
+
+ ShareChatButton(channel, accountViewModel, nav)
+
+ if (isMe) {
+ EditButton(channel, accountViewModel, nav)
+ }
+
+ WatchChannelFollows(channel, accountViewModel) { isFollowing ->
+ if (isFollowing) {
+ LeaveChatButton(channel, accountViewModel, nav)
+ }
+ }
+}
+
+@Composable
+fun WatchChannelFollows(
+ channel: PublicChatChannel,
+ accountViewModel: AccountViewModel,
+ content: @Composable (Boolean) -> Unit,
+) {
+ val isFollowing by
+ accountViewModel
+ .userProfile()
+ .live()
+ .follows
+ .map { it.user.latestContactList?.isTaggedEvent(channel.idHex) ?: false }
+ .distinctUntilChanged()
+ .observeAsState(
+ accountViewModel.userProfile().latestContactList?.isTaggedEvent(channel.idHex) ?: false,
+ )
+
+ content(isFollowing)
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/PublicChatTopBar.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/PublicChatTopBar.kt
index a4a42d252..2f034d451 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/PublicChatTopBar.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/PublicChatTopBar.kt
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28
import androidx.compose.runtime.Composable
import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.ui.navigation.EmptyNav.popBack
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.TopBarExtensibleWithBackButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt
index f7da4d439..ea1461986 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/ShortPublicChatChannelHeader.kt
@@ -23,8 +23,10 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -38,12 +40,18 @@ import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.navigation.INav
+import com.vitorpamplona.amethyst.ui.note.LikeReaction
+import com.vitorpamplona.amethyst.ui.note.ZapReaction
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions.JoinChatButton
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.HeaderPictureModifier
+import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
import com.vitorpamplona.amethyst.ui.theme.Size35dp
+import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
@Composable
fun ShortPublicChatChannelHeader(
@@ -94,3 +102,36 @@ fun ShortPublicChatChannelHeader(
}
}
}
+
+@Composable
+fun ShortChannelActionOptions(
+ channel: PublicChatChannel,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ LoadNote(baseNoteHex = channel.idHex, accountViewModel) {
+ it?.let {
+ Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = RowColSpacing) {
+ LikeReaction(
+ baseNote = it,
+ grayTint = MaterialTheme.colorScheme.onSurface,
+ accountViewModel = accountViewModel,
+ nav,
+ )
+ ZapReaction(
+ baseNote = it,
+ grayTint = MaterialTheme.colorScheme.onSurface,
+ accountViewModel = accountViewModel,
+ nav = nav,
+ )
+ Spacer(modifier = StdHorzSpacer)
+ }
+ }
+ }
+
+ WatchChannelFollows(channel, accountViewModel) { isFollowing ->
+ if (!isFollowing) {
+ JoinChatButton(channel, accountViewModel, nav)
+ }
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/EditChatButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/EditChatButton.kt
index 2897570b8..a1e50c8b2 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/EditChatButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/EditChatButton.kt
@@ -24,20 +24,18 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.EditNote
-import androidx.compose.material3.Button
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.ui.navigation.EmptyNav.nav
+import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.metadata.ChannelMetadataDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
@@ -45,23 +43,17 @@ import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
fun EditButton(
channel: PublicChatChannel,
accountViewModel: AccountViewModel,
+ nav: INav,
) {
- var wantsToPost by remember { mutableStateOf(false) }
-
- if (wantsToPost) {
- ChannelMetadataDialog({ wantsToPost = false }, accountViewModel, channel)
- }
-
- Button(
+ FilledTonalButton(
modifier =
Modifier
.padding(horizontal = 3.dp)
.width(50.dp),
- onClick = { wantsToPost = true },
+ onClick = { nav.nav("ChannelMetadataEdit?id=${channel.idHex}") },
contentPadding = ZeroPadding,
) {
Icon(
- tint = Color.White,
imageVector = Icons.Default.EditNote,
contentDescription = stringRes(R.string.edits_the_channel_metadata),
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/JoinChatButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/JoinChatButton.kt
index 35dbf3965..9d6af6e7e 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/JoinChatButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/JoinChatButton.kt
@@ -20,10 +20,9 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions
-import androidx.compose.material3.Button
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.Color
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.PublicChatChannel
import com.vitorpamplona.amethyst.ui.navigation.INav
@@ -38,11 +37,11 @@ fun JoinChatButton(
accountViewModel: AccountViewModel,
nav: INav,
) {
- Button(
+ FilledTonalButton(
modifier = HalfHalfHorzModifier,
onClick = { accountViewModel.follow(channel) },
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(R.string.join), color = Color.White)
+ Text(text = stringRes(R.string.join))
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/LeaveChatButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/LeaveChatButton.kt
index c91f1c045..0468dae83 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/LeaveChatButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/LeaveChatButton.kt
@@ -20,10 +20,9 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions
-import androidx.compose.material3.Button
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.Color
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.PublicChatChannel
import com.vitorpamplona.amethyst.ui.navigation.INav
@@ -38,11 +37,11 @@ fun LeaveChatButton(
accountViewModel: AccountViewModel,
nav: INav,
) {
- Button(
+ FilledTonalButton(
modifier = HalfHalfHorzModifier,
onClick = { accountViewModel.unfollow(channel) },
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(R.string.leave), color = Color.White)
+ Text(text = stringRes(R.string.leave))
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/LinkChatButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/LinkChatButton.kt
new file mode 100644
index 000000000..7d9949609
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/LinkChatButton.kt
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions
+
+import android.content.Intent
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ContentCopy
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat
+import androidx.core.net.toUri
+import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.ui.navigation.INav
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.stringRes
+import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
+import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
+
+@Composable
+fun LinkChatButton(
+ channel: PublicChatChannel,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ val context = LocalContext.current
+
+ FilledTonalButton(
+ modifier =
+ Modifier
+ .padding(horizontal = 3.dp)
+ .width(50.dp),
+ onClick = {
+ val intent = Intent(Intent.ACTION_VIEW, channel.toNostrUri().toUri())
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+
+ ContextCompat.startActivity(context, intent, null)
+
+ val sendIntent =
+ Intent().apply {
+ action = Intent.ACTION_SEND
+ type = "text/plain"
+ putExtra(Intent.EXTRA_TEXT, channel.toNostrUri())
+ putExtra(Intent.EXTRA_TITLE, stringRes(context, R.string.quick_action_share_browser_link))
+ }
+
+ val shareIntent =
+ Intent.createChooser(
+ sendIntent,
+ stringRes(context, R.string.quick_action_copy_note_id),
+ )
+
+ ContextCompat.startActivity(context, shareIntent, null)
+ },
+ contentPadding = ZeroPadding,
+ ) {
+ Icon(
+ imageVector = Icons.Default.ContentCopy,
+ contentDescription = stringRes(R.string.quick_action_copy_note_id),
+ modifier = Size20Modifier,
+ )
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/OpenChatButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/OpenChatButton.kt
new file mode 100644
index 000000000..01cf7c51c
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/OpenChatButton.kt
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions
+
+import android.content.Intent
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.OpenInNew
+import androidx.compose.material.icons.filled.OpenInNew
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat
+import androidx.core.net.toUri
+import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.ui.navigation.INav
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.stringRes
+import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
+import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
+
+@Composable
+fun OpenChatButton(
+ channel: PublicChatChannel,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ val context = LocalContext.current
+
+ FilledTonalButton(
+ modifier =
+ Modifier
+ .padding(horizontal = 3.dp)
+ .width(50.dp),
+ onClick = {
+ val intent = Intent(Intent.ACTION_VIEW, channel.toNostrUri().toUri())
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+
+ ContextCompat.startActivity(context, intent, null)
+ },
+ contentPadding = ZeroPadding,
+ ) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.OpenInNew,
+ contentDescription = stringRes(R.string.quick_actions_open_in_another_app),
+ modifier = Size20Modifier,
+ )
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/ShareChatButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/ShareChatButton.kt
new file mode 100644
index 000000000..a86daef86
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/header/actions/ShareChatButton.kt
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions
+
+import android.content.Intent
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Share
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat
+import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.ui.navigation.INav
+import com.vitorpamplona.amethyst.ui.note.njumpLink
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.stringRes
+import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
+import com.vitorpamplona.amethyst.ui.theme.ZeroPadding
+
+@Composable
+fun ShareChatButton(
+ channel: PublicChatChannel,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ val context = LocalContext.current
+
+ FilledTonalButton(
+ modifier =
+ Modifier
+ .padding(horizontal = 3.dp)
+ .width(50.dp),
+ onClick = {
+ val sendIntent =
+ Intent().apply {
+ action = Intent.ACTION_SEND
+ type = "text/plain"
+ putExtra(Intent.EXTRA_TEXT, njumpLink(channel.toNEvent()))
+ putExtra(Intent.EXTRA_TITLE, stringRes(context, R.string.quick_action_share_browser_link))
+ }
+
+ val shareIntent =
+ Intent.createChooser(
+ sendIntent,
+ stringRes(context, R.string.quick_action_share),
+ )
+
+ ContextCompat.startActivity(context, shareIntent, null)
+ },
+ contentPadding = ZeroPadding,
+ ) {
+ Icon(
+ imageVector = Icons.Default.Share,
+ contentDescription = stringRes(R.string.quick_action_share),
+ modifier = Size20Modifier,
+ )
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataDialog.kt
deleted file mode 100644
index 3cc03879f..000000000
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataDialog.kt
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * Copyright (c) 2024 Vitor Pamplona
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of
- * this software and associated documentation files (the "Software"), to deal in
- * the Software without restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
- * Software, and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.metadata
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.LocalTextStyle
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.input.KeyboardCapitalization
-import androidx.compose.ui.text.style.TextDirection
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.window.Dialog
-import androidx.compose.ui.window.DialogProperties
-import androidx.lifecycle.viewmodel.compose.viewModel
-import com.vitorpamplona.amethyst.R
-import com.vitorpamplona.amethyst.model.PublicChatChannel
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.PostButton
-import com.vitorpamplona.amethyst.ui.stringRes
-import com.vitorpamplona.amethyst.ui.theme.placeholderText
-
-@Composable
-fun ChannelMetadataDialog(
- onClose: () -> Unit,
- accountViewModel: AccountViewModel,
- channel: PublicChatChannel? = null,
-) {
- val postViewModel: ChannelMetadataViewModel = viewModel()
- postViewModel.load(accountViewModel.account, channel)
-
- Dialog(
- onDismissRequest = { onClose() },
- properties =
- DialogProperties(
- dismissOnClickOutside = false,
- ),
- ) {
- DialogContent(postViewModel, onClose)
- }
-}
-
-@Composable
-private fun DialogContent(
- postViewModel: ChannelMetadataViewModel,
- onClose: () -> Unit,
-) {
- Surface {
- Column(
- modifier =
- Modifier
- .padding(10.dp)
- .verticalScroll(rememberScrollState()),
- ) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- CloseButton(
- onPress = {
- postViewModel.clear()
- onClose()
- },
- )
-
- PostButton(
- onPost = {
- postViewModel.create()
- onClose()
- },
- postViewModel.channelName.value.text
- .isNotBlank(),
- )
- }
-
- Spacer(modifier = Modifier.height(15.dp))
-
- OutlinedTextField(
- label = { Text(text = stringRes(R.string.channel_name)) },
- modifier = Modifier.fillMaxWidth(),
- value = postViewModel.channelName.value,
- onValueChange = { postViewModel.channelName.value = it },
- placeholder = {
- Text(
- text = stringRes(R.string.my_awesome_group),
- color = MaterialTheme.colorScheme.placeholderText,
- )
- },
- keyboardOptions =
- KeyboardOptions.Default.copy(
- capitalization = KeyboardCapitalization.Sentences,
- ),
- textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
- )
-
- Spacer(modifier = Modifier.height(15.dp))
-
- OutlinedTextField(
- label = { Text(text = stringRes(R.string.picture_url)) },
- modifier = Modifier.fillMaxWidth(),
- value = postViewModel.channelPicture.value,
- onValueChange = { postViewModel.channelPicture.value = it },
- placeholder = {
- Text(
- text = "http://mygroup.com/logo.jpg",
- color = MaterialTheme.colorScheme.placeholderText,
- )
- },
- )
-
- Spacer(modifier = Modifier.height(15.dp))
-
- OutlinedTextField(
- label = { Text(text = stringRes(R.string.description)) },
- modifier =
- Modifier
- .fillMaxWidth()
- .height(100.dp),
- value = postViewModel.channelDescription.value,
- onValueChange = { postViewModel.channelDescription.value = it },
- placeholder = {
- Text(
- text = stringRes(R.string.about_us),
- color = MaterialTheme.colorScheme.placeholderText,
- )
- },
- keyboardOptions =
- KeyboardOptions.Default.copy(
- capitalization = KeyboardCapitalization.Sentences,
- ),
- textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
- maxLines = 10,
- )
- }
- }
-}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataScreen.kt
new file mode 100644
index 000000000..715f8b1af
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataScreen.kt
@@ -0,0 +1,318 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.metadata
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.consumeWindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.imePadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.input.KeyboardCapitalization
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextDirection
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.ui.actions.uploads.SelectSingleFromGallery
+import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
+import com.vitorpamplona.amethyst.ui.navigation.INav
+import com.vitorpamplona.amethyst.ui.navigation.routeFor
+import com.vitorpamplona.amethyst.ui.note.LoadChannel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.CreateButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.SettingsCategory
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayUrlEditField
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
+import com.vitorpamplona.amethyst.ui.stringRes
+import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
+import com.vitorpamplona.amethyst.ui.theme.MinHorzSpacer
+import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
+import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
+import com.vitorpamplona.amethyst.ui.theme.placeholderText
+import com.vitorpamplona.quartz.nip01Core.core.HexKey
+
+@Composable
+fun ChannelMetadataScreen(
+ channelId: HexKey? = null,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ if (channelId == null) {
+ ChannelMetadataScreen(null as PublicChatChannel?, accountViewModel, nav)
+ } else {
+ LoadChannel(channelId, accountViewModel) {
+ if (it is PublicChatChannel) {
+ ChannelMetadataScreen(it, accountViewModel, nav)
+ }
+ }
+ }
+}
+
+@Composable
+fun ChannelMetadataScreen(
+ channel: PublicChatChannel? = null,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ val postViewModel: ChannelMetadataViewModel = viewModel()
+ postViewModel.load(accountViewModel.account, channel)
+
+ ChannelMetadataScaffold(
+ postViewModel = postViewModel,
+ accountViewModel = accountViewModel,
+ nav = nav,
+ )
+}
+
+@Preview
+@Composable
+private fun DialogContentPreview() {
+ val accountViewModel = mockAccountViewModel()
+ val postViewModel: ChannelMetadataViewModel = viewModel()
+ postViewModel.load(accountViewModel.account, null)
+
+ ThemeComparisonColumn {
+ ChannelMetadataScaffold(
+ postViewModel = postViewModel,
+ accountViewModel = accountViewModel,
+ nav = EmptyNav,
+ )
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun ChannelMetadataScaffold(
+ postViewModel: ChannelMetadataViewModel,
+ accountViewModel: AccountViewModel,
+ nav: INav,
+) {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Spacer(modifier = MinHorzSpacer)
+
+ Text(
+ text = stringRes(R.string.public_chat),
+ modifier = Modifier.weight(1f),
+ textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.titleLarge,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
+ )
+
+ if (postViewModel.isNewChannel()) {
+ CreateButton(
+ onPost = {
+ postViewModel.createOrUpdate {
+ nav.nav(routeFor(it))
+ }
+ nav.popBack()
+ },
+ postViewModel.canPost,
+ )
+ } else {
+ SaveButton(
+ onPost = {
+ postViewModel.createOrUpdate { }
+ nav.popBack()
+ },
+ postViewModel.canPost,
+ )
+ }
+ }
+ },
+ navigationIcon = {
+ Row {
+ Spacer(modifier = StdHorzSpacer)
+ CloseButton(
+ onPress = {
+ postViewModel.clear()
+ nav.popBack()
+ },
+ )
+ }
+ },
+ colors =
+ TopAppBarDefaults.topAppBarColors(
+ containerColor = MaterialTheme.colorScheme.surface,
+ ),
+ )
+ },
+ ) { pad ->
+ val feedState by postViewModel.channelRelays.collectAsStateWithLifecycle()
+
+ LazyColumn(
+ Modifier
+ .fillMaxSize()
+ .padding(
+ start = 10.dp,
+ end = 10.dp,
+ top = pad.calculateTopPadding(),
+ bottom = pad.calculateBottomPadding(),
+ ).consumeWindowInsets(pad)
+ .imePadding(),
+ ) {
+ item {
+ SettingsCategory(
+ stringRes(R.string.public_chat_title),
+ stringRes(R.string.public_chat_explainer),
+ Modifier.padding(bottom = 8.dp),
+ )
+
+ ChannelName(postViewModel)
+
+ Spacer(modifier = DoubleVertSpacer)
+
+ Picture(postViewModel, accountViewModel)
+
+ Spacer(modifier = DoubleVertSpacer)
+
+ Description(postViewModel)
+
+ SettingsCategory(
+ stringRes(R.string.public_chat_relays_title),
+ stringRes(R.string.public_chat_relays_explainer),
+ )
+ }
+
+ itemsIndexed(feedState, key = { _, item -> "ChatRelays" + item.url }) { index, item ->
+ BasicRelaySetupInfoDialog(
+ item,
+ onDelete = { postViewModel.deleteHomeRelay(item) },
+ accountViewModel = accountViewModel,
+ nav,
+ )
+ }
+
+ item {
+ RelayUrlEditField { postViewModel.addHomeRelay(relaySetupInfoBuilder(it)) }
+
+ Spacer(modifier = DoubleVertSpacer)
+ }
+ }
+ }
+}
+
+@Composable
+private fun Description(postViewModel: ChannelMetadataViewModel) {
+ OutlinedTextField(
+ label = { Text(text = stringRes(R.string.description)) },
+ modifier = Modifier.fillMaxWidth(),
+ value = postViewModel.channelDescription.value,
+ onValueChange = { postViewModel.channelDescription.value = it },
+ placeholder = {
+ Text(
+ text = stringRes(R.string.about_us),
+ color = MaterialTheme.colorScheme.placeholderText,
+ )
+ },
+ keyboardOptions =
+ KeyboardOptions.Default.copy(
+ capitalization = KeyboardCapitalization.Sentences,
+ ),
+ textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
+ minLines = 3,
+ )
+}
+
+@Composable
+private fun Picture(
+ postViewModel: ChannelMetadataViewModel,
+ accountViewModel: AccountViewModel,
+) {
+ OutlinedTextField(
+ label = { Text(text = stringRes(R.string.picture_url)) },
+ modifier = Modifier.fillMaxWidth(),
+ value = postViewModel.channelPicture.value,
+ onValueChange = { postViewModel.channelPicture.value = it },
+ placeholder = {
+ Text(
+ text = "http://mygroup.com/logo.jpg",
+ color = MaterialTheme.colorScheme.placeholderText,
+ )
+ },
+ leadingIcon = {
+ val context = LocalContext.current
+ SelectSingleFromGallery(
+ isUploading = postViewModel.isUploadingImageForPicture,
+ tint = MaterialTheme.colorScheme.placeholderText,
+ modifier = Modifier.padding(start = 2.dp),
+ ) {
+ postViewModel.uploadForPicture(it, context, onError = accountViewModel.toastManager::toast)
+ }
+ },
+ )
+}
+
+@Composable
+private fun ChannelName(postViewModel: ChannelMetadataViewModel) {
+ OutlinedTextField(
+ label = { Text(text = stringRes(R.string.channel_name)) },
+ modifier = Modifier.fillMaxWidth(),
+ value = postViewModel.channelName.value,
+ onValueChange = { postViewModel.channelName.value = it },
+ placeholder = {
+ Text(
+ text = stringRes(R.string.my_awesome_group),
+ color = MaterialTheme.colorScheme.placeholderText,
+ )
+ },
+ keyboardOptions =
+ KeyboardOptions.Default.copy(
+ capitalization = KeyboardCapitalization.Sentences,
+ ),
+ textStyle = LocalTextStyle.current.copy(textDirection = TextDirection.Content),
+ )
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataViewModel.kt
index b16d9317c..85993cecd 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataViewModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/nip28PublicChat/metadata/ChannelMetadataViewModel.kt
@@ -20,18 +20,40 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.metadata
+import android.content.Context
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Account
+import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.PublicChatChannel
+import com.vitorpamplona.amethyst.service.uploads.CompressorQuality
+import com.vitorpamplona.amethyst.service.uploads.MediaCompressor
+import com.vitorpamplona.amethyst.service.uploads.blossom.BlossomUploader
+import com.vitorpamplona.amethyst.service.uploads.nip96.Nip96Uploader
+import com.vitorpamplona.amethyst.ui.actions.mediaServers.ServerType
+import com.vitorpamplona.amethyst.ui.actions.uploads.SelectedMedia
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
+import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.nip01Core.hints.EventHintBundle
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
+import kotlin.collections.isNotEmpty
+import kotlin.collections.map
+import kotlin.collections.plus
+import kotlin.coroutines.cancellation.CancellationException
class ChannelMetadataViewModel : ViewModel() {
private var account: Account? = null
@@ -41,6 +63,15 @@ class ChannelMetadataViewModel : ViewModel() {
val channelPicture = mutableStateOf(TextFieldValue())
val channelDescription = mutableStateOf(TextFieldValue())
+ private val _channelRelays = MutableStateFlow>(emptyList())
+ val channelRelays = _channelRelays.asStateFlow()
+
+ var isUploadingImageForPicture by mutableStateOf(false)
+
+ val canPost by derivedStateOf {
+ channelName.value.text.isNotBlank()
+ }
+
fun load(
account: Account,
channel: PublicChatChannel?,
@@ -51,10 +82,19 @@ class ChannelMetadataViewModel : ViewModel() {
channelName.value = TextFieldValue(channel.info.name ?: "")
channelPicture.value = TextFieldValue(channel.info.picture ?: "")
channelDescription.value = TextFieldValue(channel.info.about ?: "")
+
+ val relays =
+ channel.info.relays
+ ?.map { relaySetupInfoBuilder(it) }
+ ?.distinctBy { it.url }
+
+ _channelRelays.update { relays ?: emptyList() }
}
}
- fun create() {
+ fun isNewChannel() = originalChannel == null && _channelRelays.value.isNotEmpty()
+
+ fun createOrUpdate(onDone: (PublicChatChannel) -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
account?.let { account ->
val channel = originalChannel
@@ -64,10 +104,21 @@ class ChannelMetadataViewModel : ViewModel() {
channelName.value.text,
channelDescription.value.text,
channelPicture.value.text,
- null,
+ channelRelays.value.map { it.url },
)
- account.sendCreateNewChannel(template)
+ account.signAndSendPrivatelyOrBroadcast(
+ template,
+ relayList = { it.channelInfo().relays },
+ onDone = {
+ val channel = LocalCache.getOrCreateChannel(it.id) { PublicChatChannel(it) }
+ // follows the channel
+ account.follow(channel)
+ if (channel is PublicChatChannel) {
+ onDone(channel)
+ }
+ },
+ )
} else {
val event = channel.event
@@ -79,7 +130,7 @@ class ChannelMetadataViewModel : ViewModel() {
channelName.value.text,
channelDescription.value.text,
channelPicture.value.text,
- null,
+ channelRelays.value.map { it.url },
hint,
)
} else {
@@ -89,12 +140,21 @@ class ChannelMetadataViewModel : ViewModel() {
channelName.value.text,
channelDescription.value.text,
channelPicture.value.text,
- null,
+ channelRelays.value.map { it.url },
eTag,
)
}
- account.sendChangeChannel(template)
+ account.signAndSendPrivatelyOrBroadcast(
+ template,
+ relayList = { it.channelInfo().relays },
+ onDone = {
+ val channel = LocalCache.getOrCreateChannel(it.id) { PublicChatChannel(it) }
+ if (channel is PublicChatChannel) {
+ onDone(channel)
+ }
+ },
+ )
}
}
@@ -102,9 +162,91 @@ class ChannelMetadataViewModel : ViewModel() {
}
}
+ fun addHomeRelay(relay: BasicRelaySetupInfo) {
+ if (_channelRelays.value.any { it.url == relay.url }) return
+
+ _channelRelays.update { it.plus(relay) }
+ }
+
+ fun deleteHomeRelay(relay: BasicRelaySetupInfo) {
+ _channelRelays.update { it.minus(relay) }
+ }
+
fun clear() {
channelName.value = TextFieldValue()
channelPicture.value = TextFieldValue()
channelDescription.value = TextFieldValue()
+ _channelRelays.update { emptyList() }
+ }
+
+ fun uploadForPicture(
+ uri: SelectedMedia,
+ context: Context,
+ onError: (String, String) -> Unit,
+ ) {
+ viewModelScope.launch(Dispatchers.IO) {
+ upload(
+ uri,
+ context,
+ onUploading = { isUploadingImageForPicture = it },
+ onUploaded = { channelPicture.value = TextFieldValue(it) },
+ onError = onError,
+ )
+ }
+ }
+
+ private suspend fun upload(
+ galleryUri: SelectedMedia,
+ context: Context,
+ onUploading: (Boolean) -> Unit,
+ onUploaded: (String) -> Unit,
+ onError: (String, String) -> Unit,
+ ) {
+ val account = account ?: return
+ onUploading(true)
+
+ val compResult = MediaCompressor().compress(galleryUri.uri, galleryUri.mimeType, CompressorQuality.MEDIUM, context.applicationContext)
+
+ try {
+ val result =
+ if (account.settings.defaultFileServer.type == ServerType.NIP96) {
+ Nip96Uploader().upload(
+ uri = compResult.uri,
+ contentType = compResult.contentType,
+ size = compResult.size,
+ alt = null,
+ sensitiveContent = null,
+ serverBaseUrl = account.settings.defaultFileServer.baseUrl,
+ forceProxy = account::shouldUseTorForNIP96,
+ onProgress = {},
+ httpAuth = account::createHTTPAuthorization,
+ context = context,
+ )
+ } else {
+ BlossomUploader().upload(
+ uri = compResult.uri,
+ contentType = compResult.contentType,
+ size = compResult.size,
+ alt = null,
+ sensitiveContent = null,
+ serverBaseUrl = account.settings.defaultFileServer.baseUrl,
+ forceProxy = account::shouldUseTorForNIP96,
+ httpAuth = account::createBlossomUploadAuth,
+ context = context,
+ )
+ }
+
+ if (result.url != null) {
+ onUploading(false)
+ onUploaded(result.url)
+ } else {
+ onUploading(false)
+ onError(stringRes(context, R.string.failed_to_upload_media_no_details), stringRes(context, R.string.server_did_not_provide_a_url_after_uploading))
+ }
+ } catch (e: Exception) {
+ if (e is CancellationException) throw e
+ onUploading(false)
+ onError(stringRes(context, R.string.failed_to_upload_media_no_details), e.message ?: e.javaClass.simpleName)
+ }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt
index 96f6dfaa8..f80c62720 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt
@@ -22,6 +22,8 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.send
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
@@ -33,6 +35,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.style.TextDirection
+import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.actions.UrlUserTagTransformation
import com.vitorpamplona.amethyst.ui.actions.uploads.SelectFromGallery
@@ -45,7 +48,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.utils.DisplayReplying
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.utils.ThinSendButton
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.EditFieldBorder
-import com.vitorpamplona.amethyst.ui.theme.EditFieldLeadingIconModifier
import com.vitorpamplona.amethyst.ui.theme.EditFieldModifier
import com.vitorpamplona.amethyst.ui.theme.EditFieldTrailingIconModifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText
@@ -139,7 +141,7 @@ fun EditFieldRow(
SelectFromGallery(
isUploading = channelScreenModel.isUploadingImage,
tint = MaterialTheme.colorScheme.placeholderText,
- modifier = EditFieldLeadingIconModifier,
+ modifier = Modifier.height(32.dp).padding(start = 2.dp),
onImageChosen = channelScreenModel::pickedMedia,
)
},
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChannelFabColumn.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChannelFabColumn.kt
index e2984d219..d3cfb9a4e 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChannelFabColumn.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/ChannelFabColumn.kt
@@ -48,27 +48,17 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.ui.navigation.EmptyNav.nav
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.buildNewPostRoute
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
-import com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.metadata.ChannelMetadataDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Font12SP
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
@Composable
-fun ChannelFabColumn(
- accountViewModel: AccountViewModel,
- nav: INav,
-) {
+fun ChannelFabColumn(nav: INav) {
var isOpen by remember { mutableStateOf(false) }
- var wantsToCreateChannel by remember { mutableStateOf(false) }
-
- if (wantsToCreateChannel) {
- ChannelMetadataDialog({ wantsToCreateChannel = false }, accountViewModel = accountViewModel)
- }
-
Column {
AnimatedVisibility(
visible = isOpen,
@@ -101,7 +91,7 @@ fun ChannelFabColumn(
FloatingActionButton(
onClick = {
- wantsToCreateChannel = true
+ nav.nav("ChannelMetadataEdit")
isOpen = false
},
modifier = Size55Modifier,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/singlepane/MessagesSinglePane.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/singlepane/MessagesSinglePane.kt
index df423756d..3d64f1e61 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/singlepane/MessagesSinglePane.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/singlepane/MessagesSinglePane.kt
@@ -90,7 +90,7 @@ fun MessagesSinglePane(
}
},
floatingButton = {
- ChannelFabColumn(accountViewModel, nav)
+ ChannelFabColumn(nav)
},
accountViewModel = accountViewModel,
) {
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt
index 2d88c2115..5902df4c2 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/rooms/twopane/MessagesTwoPane.kt
@@ -97,7 +97,7 @@ fun MessagesTwoPane(
)
Box(Modifier.padding(Size20dp), contentAlignment = Alignment.Center) {
- ChannelFabColumn(accountViewModel, nav)
+ ChannelFabColumn(nav)
}
}
},
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/FollowButtons.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/FollowButtons.kt
index 4f3d73f39..4416a934c 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/FollowButtons.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/FollowButtons.kt
@@ -21,13 +21,10 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
@@ -40,32 +37,24 @@ fun FollowButton(
text: Int = R.string.follow,
onClick: () -> Unit,
) {
- Button(
+ FilledTonalButton(
modifier = Modifier.padding(start = 3.dp),
onClick = onClick,
shape = LeftHalfCircleButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary,
- ),
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(text), color = Color.White, textAlign = TextAlign.Center)
+ Text(text = stringRes(text), textAlign = TextAlign.Center)
}
}
@Composable
fun UnfollowButton(onClick: () -> Unit) {
- Button(
+ FilledTonalButton(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onClick,
shape = LeftHalfCircleButtonBorder,
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary,
- ),
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(R.string.unfollow), color = Color.White)
+ Text(text = stringRes(R.string.unfollow))
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/EditButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/EditButton.kt
index ab162e830..c14d0d78d 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/EditButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/EditButton.kt
@@ -24,11 +24,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.EditNote
-import androidx.compose.material3.Button
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
@@ -50,7 +49,7 @@ fun InnerEditButtonPreview() {
@Composable
fun InnerEditButton(onClick: () -> Unit) {
- Button(
+ FilledTonalButton(
modifier =
Modifier
.padding(horizontal = 3.dp)
@@ -59,7 +58,6 @@ fun InnerEditButton(onClick: () -> Unit) {
contentPadding = ZeroPadding,
) {
Icon(
- tint = Color.White,
imageVector = Icons.Default.EditNote,
contentDescription = stringRes(R.string.edits_the_user_s_metadata),
)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/MessageButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/MessageButton.kt
index 705575cdd..36a32d006 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/MessageButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/header/MessageButton.kt
@@ -22,12 +22,11 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.header
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
-import androidx.compose.material3.Button
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
@@ -48,7 +47,7 @@ fun MessageButton(
) {
val scope = rememberCoroutineScope()
- Button(
+ FilledTonalButton(
modifier =
Modifier
.padding(horizontal = 3.dp)
@@ -62,7 +61,6 @@ fun MessageButton(
painter = painterResource(R.drawable.ic_dm),
stringRes(R.string.send_a_direct_message),
modifier = Size20Modifier,
- tint = Color.White,
)
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ShowUserButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ShowUserButton.kt
index 7def995e8..45d5c3512 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ShowUserButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/zaps/ShowUserButton.kt
@@ -21,13 +21,12 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.zaps
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.stringRes
@@ -36,7 +35,7 @@ import com.vitorpamplona.amethyst.ui.theme.ButtonPadding
@Composable
fun ShowUserButton(onClick: () -> Unit) {
- Button(
+ FilledTonalButton(
modifier = Modifier.padding(start = 3.dp),
onClick = onClick,
shape = ButtonBorder,
@@ -46,6 +45,6 @@ fun ShowUserButton(onClick: () -> Unit) {
),
contentPadding = ButtonPadding,
) {
- Text(text = stringRes(R.string.unblock), color = Color.White)
+ Text(text = stringRes(R.string.unblock))
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/ShowQRDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/ShowQRDialog.kt
index 7d360ad6f..de685a6e8 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/ShowQRDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/qrcode/ShowQRDialog.kt
@@ -20,22 +20,26 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode
-import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -43,7 +47,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
@@ -57,6 +60,7 @@ import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.components.CreateTextWithEmoji
import com.vitorpamplona.amethyst.ui.components.DisplayNIP05
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
+import com.vitorpamplona.amethyst.ui.components.SetDialogToEdgeToEdge
import com.vitorpamplona.amethyst.ui.components.nip05VerificationAsAState
import com.vitorpamplona.amethyst.ui.note.ArrowBackIcon
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -65,6 +69,8 @@ import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size35dp
+import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
+import com.vitorpamplona.amethyst.ui.theme.largeProfilePictureModifier
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
@Preview
@@ -98,6 +104,7 @@ fun BackButton(onPress: () -> Unit) {
}
}
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ShowQRDialog(
user: User,
@@ -111,96 +118,113 @@ fun ShowQRDialog(
onDismissRequest = onClose,
properties = DialogProperties(usePlatformDefaultWidth = false),
) {
- Surface {
- Column {
- Row(
- modifier = Modifier.padding(10.dp),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- BackButton(onPress = onClose)
- }
+ SetDialogToEdgeToEdge()
- Column(
- modifier = Modifier.fillMaxSize().padding(horizontal = 10.dp),
- verticalArrangement = Arrangement.SpaceAround,
- ) {
- if (presenting) {
- Column {
- Row(
- horizontalArrangement = Arrangement.Center,
- modifier = Modifier.fillMaxWidth(),
- ) {
- RobohashFallbackAsyncImage(
- robot = user.pubkeyHex,
- model = user.profilePicture(),
- contentDescription = stringRes(R.string.profile_image),
- modifier =
- Modifier
- .width(120.dp)
- .height(120.dp)
- .clip(shape = CircleShape)
- .border(3.dp, MaterialTheme.colorScheme.onBackground, CircleShape),
- loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
- loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
- )
- }
- Row(
- horizontalArrangement = Arrangement.Center,
- modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
- ) {
- CreateTextWithEmoji(
- text = user.info?.bestName() ?: "",
- tags = user.info?.tags,
- fontWeight = FontWeight.Bold,
- fontSize = 20.sp,
- )
- }
-
- Row(
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center,
- modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
- ) {
- val nip05 = user.nip05()
- if (nip05 != null) {
- val nip05Verified =
- nip05VerificationAsAState(user.info!!, user.pubkeyHex, accountViewModel)
-
- DisplayNIP05(nip05, nip05Verified, accountViewModel)
- } else {
- Text(
- text = user.pubkeyDisplayHex(),
- fontSize = Font14SP,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = {},
+ navigationIcon = {
+ Row {
+ Spacer(modifier = StdHorzSpacer)
+ BackButton(onPress = onClose)
+ }
+ },
+ colors =
+ TopAppBarDefaults.topAppBarColors(
+ containerColor = MaterialTheme.colorScheme.surface,
+ ),
+ )
+ },
+ ) { pad ->
+ Surface(
+ Modifier
+ .fillMaxSize()
+ .padding(
+ start = 10.dp,
+ end = 10.dp,
+ top = pad.calculateTopPadding(),
+ bottom = pad.calculateBottomPadding(),
+ ).consumeWindowInsets(pad)
+ .imePadding(),
+ ) {
+ Column {
+ Column(
+ modifier = Modifier.fillMaxSize().padding(horizontal = 10.dp),
+ verticalArrangement = Arrangement.SpaceAround,
+ ) {
+ if (presenting) {
+ Column {
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ RobohashFallbackAsyncImage(
+ robot = user.pubkeyHex,
+ model = user.profilePicture(),
+ contentDescription = stringRes(R.string.profile_image),
+ modifier = MaterialTheme.colorScheme.largeProfilePictureModifier,
+ loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
+ loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
)
}
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().padding(top = 10.dp),
+ ) {
+ CreateTextWithEmoji(
+ text = user.info?.bestName() ?: "",
+ tags = user.info?.tags,
+ fontWeight = FontWeight.Bold,
+ fontSize = 20.sp,
+ )
+ }
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
+ ) {
+ val nip05 = user.nip05()
+ if (nip05 != null) {
+ val nip05Verified =
+ nip05VerificationAsAState(user.info!!, user.pubkeyHex, accountViewModel)
+
+ DisplayNIP05(nip05, nip05Verified, accountViewModel)
+ } else {
+ Text(
+ text = user.pubkeyDisplayHex(),
+ fontSize = Font14SP,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ }
}
- }
- Row(
- horizontalArrangement = Arrangement.Center,
- modifier = Modifier.fillMaxWidth().padding(horizontal = Size10dp),
- ) {
- QrCodeDrawer(user.toNostrUri())
- }
-
- Row(modifier = Modifier.padding(horizontal = 30.dp)) {
- FilledTonalButton(
- onClick = { presenting = false },
- shape = RoundedCornerShape(Size35dp),
- modifier = Modifier.fillMaxWidth().height(50.dp),
+ Row(
+ horizontalArrangement = Arrangement.Center,
+ modifier = Modifier.fillMaxWidth().padding(horizontal = Size10dp),
) {
- Text(text = stringRes(R.string.scan_qr))
+ QrCodeDrawer(user.toNostrUri())
}
- }
- } else {
- NIP19QrCodeScanner {
- if (it.isNullOrEmpty()) {
- presenting = true
- } else {
- onScan(it)
+
+ Row(modifier = Modifier.padding(horizontal = 30.dp)) {
+ FilledTonalButton(
+ onClick = { presenting = false },
+ shape = RoundedCornerShape(Size35dp),
+ modifier = Modifier.fillMaxWidth().height(50.dp),
+ ) {
+ Text(text = stringRes(R.string.scan_qr))
+ }
+ }
+ } else {
+ NIP19QrCodeScanner {
+ if (it.isNullOrEmpty()) {
+ presenting = true
+ } else {
+ onScan(it)
+ }
}
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AllRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt
similarity index 88%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AllRelayListView.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt
index 24549b416..e52a465db 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AllRelayListView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/AllRelayListScreen.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -54,6 +54,21 @@ import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.DMRelayListViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm.renderDMItems
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.kind3.Kind3RelayListViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.kind3.renderKind3Items
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.kind3.renderKind3ProposalItems
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.local.LocalRelayListViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.local.renderLocalItems
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip37.PrivateOutboxRelayListViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip37.renderPrivateOutboxItems
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip65.Nip65RelayListViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip65.renderNip65HomeItems
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip65.renderNip65NotifItems
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.SearchRelayListViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search.renderSearchItems
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.MinHorzSpacer
@@ -61,10 +76,9 @@ import com.vitorpamplona.amethyst.ui.theme.RowColSpacing
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.grayText
import com.vitorpamplona.ammolite.relays.Constants
-import com.vitorpamplona.quartz.nip01Core.relay.RelayStat
@Composable
-fun AllRelayListView(
+fun AllRelayListScreen(
relayToAdd: String? = null,
accountViewModel: AccountViewModel,
nav: INav,
@@ -275,7 +289,11 @@ fun ResetSearchRelays(postViewModel: SearchRelayListViewModel) {
OutlinedButton(
onClick = {
postViewModel.deleteAll()
- DefaultSearchRelayList.forEach { postViewModel.addRelay(BasicRelaySetupInfo(it, RelayStat())) }
+ DefaultSearchRelayList.forEach {
+ postViewModel.addRelay(
+ relaySetupInfoBuilder(it),
+ )
+ }
postViewModel.loadRelayDocuments()
},
) {
@@ -288,7 +306,9 @@ fun ResetDMRelays(postViewModel: DMRelayListViewModel) {
OutlinedButton(
onClick = {
postViewModel.deleteAll()
- DefaultDMRelayList.forEach { postViewModel.addRelay(BasicRelaySetupInfo(it, RelayStat())) }
+ DefaultDMRelayList.forEach {
+ postViewModel.addRelay(relaySetupInfoBuilder(it))
+ }
postViewModel.loadRelayDocuments()
},
) {
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayInformationDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationDialog.kt
similarity index 99%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayInformationDialog.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationDialog.kt
index eeb077170..3fb474081 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayInformationDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/RelayInformationDialog.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfo.kt
new file mode 100644
index 000000000..7d29082f2
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfo.kt
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
+
+import androidx.compose.runtime.Immutable
+import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
+import com.vitorpamplona.ammolite.relays.RelayStats
+import com.vitorpamplona.quartz.nip01Core.relay.RelayStat
+import com.vitorpamplona.quartz.nip65RelayList.RelayUrlFormatter
+
+@Immutable
+data class BasicRelaySetupInfo(
+ val url: String,
+ val relayStat: RelayStat,
+ val paidRelay: Boolean = false,
+) {
+ val briefInfo: RelayBriefInfoCache.RelayBriefInfo = RelayBriefInfoCache.RelayBriefInfo(url)
+}
+
+fun relaySetupInfoBuilder(url: String): BasicRelaySetupInfo {
+ val normalized = RelayUrlFormatter.normalize(url)
+ return BasicRelaySetupInfo(
+ normalized,
+ RelayStats.get(normalized),
+ )
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoClickableRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoClickableRow.kt
similarity index 98%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoClickableRow.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoClickableRow.kt
index a13218212..d0fee4c97 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoClickableRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoClickableRow.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoDialog.kt
similarity index 97%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoDialog.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoDialog.kt
index 4cc08ed1c..66b55a623 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoDialog.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -32,6 +32,7 @@ import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.ui.actions.RelayInfoDialog
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt
similarity index 87%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoModel.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt
index a002dc8ef..7764fda06 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfoModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/BasicRelaySetupInfoModel.kt
@@ -18,14 +18,13 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
-import com.vitorpamplona.ammolite.relays.RelayStats
-import com.vitorpamplona.quartz.nip65RelayList.RelayUrlFormatter
+import com.vitorpamplona.amethyst.service.replace
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -80,12 +79,8 @@ abstract class BasicRelaySetupInfoModel : ViewModel() {
val relayList = getRelayList() ?: emptyList()
relayList
- .map { relayUrl ->
- BasicRelaySetupInfo(
- RelayUrlFormatter.normalize(relayUrl),
- RelayStats.get(relayUrl),
- )
- }.distinctBy { it.url }
+ .map { relaySetupInfoBuilder(it) }
+ .distinctBy { it.url }
.sortedBy { it.relayStat.receivedBytes }
.reversed()
}
@@ -112,6 +107,6 @@ abstract class BasicRelaySetupInfoModel : ViewModel() {
relay: BasicRelaySetupInfo,
paid: Boolean,
) {
- _relays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
+ _relays.update { it.replace(relay, relay.copy(paidRelay = paid)) }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayNameAndRemoveButton.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayNameAndRemoveButton.kt
similarity index 98%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayNameAndRemoveButton.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayNameAndRemoveButton.kt
index eeba9d2d1..8b34ea3d5 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayNameAndRemoveButton.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayNameAndRemoveButton.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayStatusRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayStatusRow.kt
similarity index 97%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayStatusRow.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayStatusRow.kt
index d5707a366..5170e7f28 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayStatusRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayStatusRow.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
import android.widget.Toast
import androidx.compose.foundation.gestures.detectTapGestures
@@ -39,6 +39,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R
+import com.vitorpamplona.amethyst.service.countToHumanReadable
+import com.vitorpamplona.amethyst.service.countToHumanReadableBytes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.allGoodColor
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayUrlEditField.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayUrlEditField.kt
similarity index 72%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayUrlEditField.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayUrlEditField.kt
index 3b33cba4c..4633e769a 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/RelayUrlEditField.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/common/RelayUrlEditField.kt
@@ -18,10 +18,12 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
@@ -35,16 +37,27 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardCapitalization
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.tooling.preview.Preview
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.Size10dp
+import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
import com.vitorpamplona.amethyst.ui.theme.placeholderText
-import com.vitorpamplona.quartz.nip01Core.relay.RelayStat
-import com.vitorpamplona.quartz.nip65RelayList.RelayUrlFormatter
+
+@Preview
+@Composable
+fun RelayUrlEditFieldPreview() {
+ ThemeComparisonColumn {
+ RelayUrlEditField {}
+ }
+}
@Composable
-fun RelayUrlEditField(onNewRelay: (BasicRelaySetupInfo) -> Unit) {
+fun RelayUrlEditField(onNewRelay: (String) -> Unit) {
var url by remember { mutableStateOf("") }
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(Size10dp)) {
@@ -61,12 +74,28 @@ fun RelayUrlEditField(onNewRelay: (BasicRelaySetupInfo) -> Unit) {
)
},
singleLine = true,
+ keyboardOptions =
+ KeyboardOptions.Default.copy(
+ autoCorrectEnabled = false,
+ imeAction = ImeAction.Go,
+ capitalization = KeyboardCapitalization.None,
+ keyboardType = KeyboardType.Text,
+ ),
+ keyboardActions =
+ KeyboardActions(
+ onGo = {
+ if (url.isNotBlank() && url != "/") {
+ onNewRelay(url)
+ url = ""
+ }
+ },
+ ),
)
Button(
onClick = {
if (url.isNotBlank() && url != "/") {
- onNewRelay(BasicRelaySetupInfo(RelayUrlFormatter.normalize(url), RelayStat()))
+ onNewRelay(url)
url = ""
}
},
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AddDMRelayListDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/AddDMRelayListDialog.kt
similarity index 95%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AddDMRelayListDialog.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/AddDMRelayListDialog.kt
index e1f480662..5cf3c83af 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AddDMRelayListDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/AddDMRelayListDialog.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -49,11 +49,11 @@ import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.imageModifier
-import com.vitorpamplona.quartz.nip01Core.relay.RelayStat
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -153,7 +153,9 @@ fun ResetDMRelaysLonger(postViewModel: DMRelayListViewModel) {
OutlinedButton(
onClick = {
postViewModel.deleteAll()
- DefaultDMRelayList.forEach { postViewModel.addRelay(BasicRelaySetupInfo(it, RelayStat())) }
+ DefaultDMRelayList.forEach {
+ postViewModel.addRelay(relaySetupInfoBuilder(it))
+ }
postViewModel.loadRelayDocuments()
},
) {
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/DMRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListView.kt
similarity index 85%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/DMRelayListView.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListView.kt
index 4b79bf2ea..353ebc033 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/DMRelayListView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListView.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -32,6 +32,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayUrlEditField
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
@@ -71,6 +75,6 @@ fun LazyListScope.renderDMItems(
item {
Spacer(modifier = StdVertSpacer)
- RelayUrlEditField { postViewModel.addRelay(it) }
+ RelayUrlEditField { postViewModel.addRelay(relaySetupInfoBuilder(it)) }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/DMRelayListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListViewModel.kt
similarity index 89%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/DMRelayListViewModel.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListViewModel.kt
index 5dff04edc..66114e113 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/DMRelayListViewModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/dm/DMRelayListViewModel.kt
@@ -18,7 +18,9 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.dm
+
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
class DMRelayListViewModel : BasicRelaySetupInfoModel() {
override fun getRelayList(): List? = account.getDMRelayList()?.relays()
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3BasicRelaySetupInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3BasicRelaySetupInfo.kt
new file mode 100644
index 000000000..ecaaf6896
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3BasicRelaySetupInfo.kt
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.kind3
+
+import androidx.compose.runtime.Immutable
+import com.vitorpamplona.ammolite.relays.FeedType
+import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
+import com.vitorpamplona.quartz.nip01Core.relay.RelayStat
+
+@Immutable
+data class Kind3BasicRelaySetupInfo(
+ val url: String,
+ val read: Boolean,
+ val write: Boolean,
+ val feedTypes: Set,
+ val relayStat: RelayStat,
+ val paidRelay: Boolean = false,
+) {
+ val briefInfo: RelayBriefInfoCache.RelayBriefInfo = RelayBriefInfoCache.RelayBriefInfo(url)
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3RelayListView.kt
similarity index 97%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListView.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3RelayListView.kt
index 5a3a1d357..6e863b1a4 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3RelayListView.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.kind3
import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -70,11 +70,16 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.FeatureSetType
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
import com.vitorpamplona.amethyst.service.Nip11Retriever
+import com.vitorpamplona.amethyst.service.countToHumanReadable
+import com.vitorpamplona.amethyst.service.countToHumanReadableBytes
import com.vitorpamplona.amethyst.ui.actions.RelayInfoDialog
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
import com.vitorpamplona.amethyst.ui.note.RenderRelayIcon
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.recommendations.Kind3RelayProposalSetupInfo
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.recommendations.Kind3RelaySetupInfoProposalDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
@@ -90,10 +95,10 @@ import com.vitorpamplona.amethyst.ui.theme.allGoodColor
import com.vitorpamplona.amethyst.ui.theme.largeRelayIconModifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.amethyst.ui.theme.warningColor
-import com.vitorpamplona.ammolite.relays.Constants
import com.vitorpamplona.ammolite.relays.Constants.activeTypesGlobalChats
import com.vitorpamplona.ammolite.relays.FeedType
import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
+import com.vitorpamplona.ammolite.relays.RelayStats
import com.vitorpamplona.quartz.nip01Core.relay.RelayStat
import com.vitorpamplona.quartz.nip65RelayList.RelayUrlFormatter
import kotlinx.coroutines.launch
@@ -184,7 +189,7 @@ fun ServerConfigPreview() {
sentBytes = 10000000,
spamCounter = 10,
),
- feedTypes = Constants.activeTypesGlobalChats,
+ feedTypes = activeTypesGlobalChats,
paidRelay = true,
),
onDelete = {},
@@ -790,13 +795,14 @@ fun Kind3RelayEditBox(
Button(
onClick = {
if (url.isNotBlank() && url != "/") {
+ val normalized = RelayUrlFormatter.normalize(url)
onNewRelay(
Kind3BasicRelaySetupInfo(
- url = RelayUrlFormatter.normalize(url),
+ url = normalized,
read = read,
write = write,
feedTypes = activeTypesGlobalChats,
- relayStat = RelayStat(),
+ relayStat = RelayStats.get(normalized),
),
)
url = ""
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3RelayListViewModel.kt
similarity index 85%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListViewModel.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3RelayListViewModel.kt
index d5f6f80c5..348d5d87b 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelayListViewModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/kind3/Kind3RelayListViewModel.kt
@@ -18,13 +18,16 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.kind3
import androidx.compose.runtime.Stable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
+import com.vitorpamplona.amethyst.service.replace
+import com.vitorpamplona.amethyst.service.togglePresenceInSet
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.recommendations.Kind3RelayProposalSetupInfo
import com.vitorpamplona.ammolite.relays.Constants
import com.vitorpamplona.ammolite.relays.Constants.activeTypesGlobalChats
import com.vitorpamplona.ammolite.relays.FeedType
@@ -38,6 +41,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
+import kotlin.collections.plus
@Stable
class Kind3RelayListViewModel : ViewModel() {
@@ -206,7 +210,18 @@ class Kind3RelayListViewModel : ViewModel() {
fun addRelay(relay: Kind3RelayProposalSetupInfo) {
if (relays.value.any { it.url == relay.url }) return
- _relays.update { it.plus(Kind3BasicRelaySetupInfo(relay.url, relay.read, relay.write, relay.feedTypes, relay.relayStat, relay.paidRelay)) }
+ _relays.update {
+ it.plus(
+ Kind3BasicRelaySetupInfo(
+ relay.url,
+ relay.read,
+ relay.write,
+ relay.feedTypes,
+ relay.relayStat,
+ relay.paidRelay,
+ ),
+ )
+ }
refreshProposals()
@@ -230,42 +245,42 @@ class Kind3RelayListViewModel : ViewModel() {
}
fun toggleDownload(relay: Kind3BasicRelaySetupInfo) {
- _relays.update { it.updated(relay, relay.copy(read = !relay.read)) }
+ _relays.update { it.replace(relay, relay.copy(read = !relay.read)) }
hasModified = true
}
fun toggleUpload(relay: Kind3BasicRelaySetupInfo) {
- _relays.update { it.updated(relay, relay.copy(write = !relay.write)) }
+ _relays.update { it.replace(relay, relay.copy(write = !relay.write)) }
hasModified = true
}
fun toggleFollows(relay: Kind3BasicRelaySetupInfo) {
- val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.FOLLOWS)
- _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
+ val newTypes = relay.feedTypes.togglePresenceInSet(FeedType.FOLLOWS)
+ _relays.update { it.replace(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
}
fun toggleMessages(relay: Kind3BasicRelaySetupInfo) {
- val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.PRIVATE_DMS)
- _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
+ val newTypes = relay.feedTypes.togglePresenceInSet(FeedType.PRIVATE_DMS)
+ _relays.update { it.replace(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
}
fun togglePublicChats(relay: Kind3BasicRelaySetupInfo) {
- val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.PUBLIC_CHATS)
- _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
+ val newTypes = relay.feedTypes.togglePresenceInSet(FeedType.PUBLIC_CHATS)
+ _relays.update { it.replace(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
}
fun toggleGlobal(relay: Kind3BasicRelaySetupInfo) {
- val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.GLOBAL)
- _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
+ val newTypes = relay.feedTypes.togglePresenceInSet(FeedType.GLOBAL)
+ _relays.update { it.replace(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
}
fun toggleSearch(relay: Kind3BasicRelaySetupInfo) {
- val newTypes = togglePresenceInSet(relay.feedTypes, FeedType.SEARCH)
- _relays.update { it.updated(relay, relay.copy(feedTypes = newTypes)) }
+ val newTypes = relay.feedTypes.togglePresenceInSet(FeedType.SEARCH)
+ _relays.update { it.replace(relay, relay.copy(feedTypes = newTypes)) }
hasModified = true
}
@@ -273,16 +288,6 @@ class Kind3RelayListViewModel : ViewModel() {
relay: Kind3BasicRelaySetupInfo,
paid: Boolean,
) {
- _relays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
+ _relays.update { it.replace(relay, relay.copy(paidRelay = paid)) }
}
}
-
-fun Iterable.updated(
- old: T,
- new: T,
-): List = map { if (it == old) new else it }
-
-fun togglePresenceInSet(
- set: Set,
- item: T,
-): Set = if (set.contains(item)) set.minus(item) else set.plus(item)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/LocalRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListView.kt
similarity index 85%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/LocalRelayListView.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListView.kt
index 47193e35e..362cd20b3 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/LocalRelayListView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListView.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.local
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -32,6 +32,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayUrlEditField
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
@@ -71,6 +75,6 @@ fun LazyListScope.renderLocalItems(
item {
Spacer(modifier = StdVertSpacer)
- RelayUrlEditField { postViewModel.addRelay(it) }
+ RelayUrlEditField { postViewModel.addRelay(relaySetupInfoBuilder(it)) }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/LocalRelayListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListViewModel.kt
similarity index 89%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/LocalRelayListViewModel.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListViewModel.kt
index efde0c10d..e010dff3f 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/LocalRelayListViewModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/local/LocalRelayListViewModel.kt
@@ -18,7 +18,9 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.local
+
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
class LocalRelayListViewModel : BasicRelaySetupInfoModel() {
override fun getRelayList(): List = account.settings.localRelayServers.toList()
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/PrivateOutboxRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListView.kt
similarity index 85%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/PrivateOutboxRelayListView.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListView.kt
index fbe1c8b18..c69f3b514 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/PrivateOutboxRelayListView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListView.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip37
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -32,6 +32,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayUrlEditField
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
@@ -71,6 +75,6 @@ fun LazyListScope.renderPrivateOutboxItems(
item {
Spacer(modifier = StdVertSpacer)
- RelayUrlEditField { postViewModel.addRelay(it) }
+ RelayUrlEditField { postViewModel.addRelay(relaySetupInfoBuilder(it)) }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/PrivateOutboxRelayListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListViewModel.kt
similarity index 89%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/PrivateOutboxRelayListViewModel.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListViewModel.kt
index 92f2c9137..69dec48da 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/PrivateOutboxRelayListViewModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip37/PrivateOutboxRelayListViewModel.kt
@@ -18,7 +18,9 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip37
+
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
class PrivateOutboxRelayListViewModel : BasicRelaySetupInfoModel() {
override fun getRelayList(): List? = account.getPrivateOutboxRelayList()?.relays()
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Nip65RelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListView.kt
similarity index 86%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Nip65RelayListView.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListView.kt
index d0c315fd2..283204936 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Nip65RelayListView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListView.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip65
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -32,6 +32,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayUrlEditField
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
@@ -74,7 +78,7 @@ fun LazyListScope.renderNip65HomeItems(
item {
Spacer(modifier = StdVertSpacer)
- RelayUrlEditField { postViewModel.addHomeRelay(it) }
+ RelayUrlEditField { postViewModel.addHomeRelay(relaySetupInfoBuilder(it)) }
}
}
@@ -95,6 +99,6 @@ fun LazyListScope.renderNip65NotifItems(
item {
Spacer(modifier = StdVertSpacer)
- RelayUrlEditField { postViewModel.addNotifRelay(it) }
+ RelayUrlEditField { postViewModel.addNotifRelay(relaySetupInfoBuilder(it)) }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Nip65RelayListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListViewModel.kt
similarity index 88%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Nip65RelayListViewModel.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListViewModel.kt
index 7306643e5..114da4bcd 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Nip65RelayListViewModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/nip65/Nip65RelayListViewModel.kt
@@ -18,16 +18,17 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.nip65
import androidx.compose.runtime.Stable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.service.Nip11CachedRetriever
-import com.vitorpamplona.ammolite.relays.RelayStats
+import com.vitorpamplona.amethyst.service.replace
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
-import com.vitorpamplona.quartz.nip65RelayList.RelayUrlFormatter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -110,12 +111,8 @@ class Nip65RelayListViewModel : ViewModel() {
val relayList = account.getNIP65RelayList()?.writeRelays() ?: emptyList()
relayList
- .map { relayUrl ->
- BasicRelaySetupInfo(
- RelayUrlFormatter.normalize(relayUrl),
- RelayStats.get(relayUrl),
- )
- }.distinctBy { it.url }
+ .map { relaySetupInfoBuilder(it) }
+ .distinctBy { it.url }
.sortedBy { it.relayStat.receivedBytes }
.reversed()
}
@@ -124,12 +121,8 @@ class Nip65RelayListViewModel : ViewModel() {
val relayList = account.getNIP65RelayList()?.readRelays() ?: emptyList()
relayList
- .map { relayUrl ->
- BasicRelaySetupInfo(
- RelayUrlFormatter.normalize(relayUrl),
- RelayStats.get(relayUrl),
- )
- }.distinctBy { it.url }
+ .map { relaySetupInfoBuilder(it) }
+ .distinctBy { it.url }
.sortedBy { it.relayStat.receivedBytes }
.reversed()
}
@@ -156,7 +149,7 @@ class Nip65RelayListViewModel : ViewModel() {
relay: BasicRelaySetupInfo,
paid: Boolean,
) {
- _homeRelays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
+ _homeRelays.update { it.replace(relay, relay.copy(paidRelay = paid)) }
}
fun addNotifRelay(relay: BasicRelaySetupInfo) {
@@ -180,6 +173,6 @@ class Nip65RelayListViewModel : ViewModel() {
relay: BasicRelaySetupInfo,
paid: Boolean,
) {
- _notificationRelays.update { it.updated(relay, relay.copy(paidRelay = paid)) }
+ _notificationRelays.update { it.replace(relay, relay.copy(paidRelay = paid)) }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfo.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelayProposalSetupInfo.kt
similarity index 74%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfo.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelayProposalSetupInfo.kt
index 0e82326b7..aa9472d0a 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/BasicRelaySetupInfo.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelayProposalSetupInfo.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.recommendations
import androidx.compose.runtime.Immutable
import com.vitorpamplona.ammolite.relays.FeedType
@@ -26,27 +26,6 @@ import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.relay.RelayStat
-@Immutable
-data class BasicRelaySetupInfo(
- val url: String,
- val relayStat: RelayStat,
- val paidRelay: Boolean = false,
-) {
- val briefInfo: RelayBriefInfoCache.RelayBriefInfo = RelayBriefInfoCache.RelayBriefInfo(url)
-}
-
-@Immutable
-data class Kind3BasicRelaySetupInfo(
- val url: String,
- val read: Boolean,
- val write: Boolean,
- val feedTypes: Set,
- val relayStat: RelayStat,
- val paidRelay: Boolean = false,
-) {
- val briefInfo: RelayBriefInfoCache.RelayBriefInfo = RelayBriefInfoCache.RelayBriefInfo(url)
-}
-
@Immutable
data class Kind3RelayProposalSetupInfo(
val url: String,
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelaySetupInfoProposalDialog.kt
similarity index 96%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalDialog.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelaySetupInfoProposalDialog.kt
index b767d58aa..295aebfc4 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelaySetupInfoProposalDialog.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.recommendations
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -32,6 +32,7 @@ import com.vitorpamplona.amethyst.service.Nip11Retriever
import com.vitorpamplona.amethyst.ui.actions.RelayInfoDialog
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.RelayInformationDialog
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelaySetupInfoProposalRow.kt
similarity index 98%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalRow.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelaySetupInfoProposalRow.kt
index c0e5233bb..3ded4f702 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/Kind3RelaySetupInfoProposalRow.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/recommendations/Kind3RelaySetupInfoProposalRow.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.recommendations
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AddSearchRelayListDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/AddSearchRelayListDialog.kt
similarity index 95%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AddSearchRelayListDialog.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/AddSearchRelayListDialog.kt
index feff3bacc..daee2f64c 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/AddSearchRelayListDialog.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/AddSearchRelayListDialog.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -49,11 +49,11 @@ import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.imageModifier
-import com.vitorpamplona.quartz.nip01Core.relay.RelayStat
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -153,7 +153,9 @@ fun ResetSearchRelaysLonger(postViewModel: SearchRelayListViewModel) {
OutlinedButton(
onClick = {
postViewModel.deleteAll()
- DefaultSearchRelayList.forEach { postViewModel.addRelay(BasicRelaySetupInfo(it, RelayStat())) }
+ DefaultSearchRelayList.forEach {
+ postViewModel.addRelay(relaySetupInfoBuilder(it))
+ }
postViewModel.loadRelayDocuments()
},
) {
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/SearchRelayListView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListView.kt
similarity index 85%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/SearchRelayListView.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListView.kt
index 61484b404..d446b4520 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/SearchRelayListView.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListView.kt
@@ -18,7 +18,7 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -32,6 +32,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.rememberExtendedNav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfo
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoDialog
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.RelayUrlEditField
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.relaySetupInfoBuilder
import com.vitorpamplona.amethyst.ui.theme.FeedPadding
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
@@ -72,6 +76,6 @@ fun LazyListScope.renderSearchItems(
item {
Spacer(modifier = StdVertSpacer)
- RelayUrlEditField { postViewModel.addRelay(it) }
+ RelayUrlEditField { postViewModel.addRelay(relaySetupInfoBuilder(it)) }
}
}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/SearchRelayListViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListViewModel.kt
similarity index 89%
rename from amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/SearchRelayListViewModel.kt
rename to amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListViewModel.kt
index cd1786216..7c469182d 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/relays/SearchRelayListViewModel.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/relays/search/SearchRelayListViewModel.kt
@@ -18,7 +18,9 @@
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-package com.vitorpamplona.amethyst.ui.actions.relays
+package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.search
+
+import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.common.BasicRelaySetupInfoModel
class SearchRelayListViewModel : BasicRelaySetupInfoModel() {
override fun getRelayList(): List? = account.getSearchRelayList()?.relays()
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Color.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Color.kt
index fc301a81d..2f1f73135 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Color.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Color.kt
@@ -24,6 +24,14 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
+val Primary50 = Color(red = 127, green = 103, blue = 190)
+val Primary60 = Color(red = 154, green = 130, blue = 219)
+val Primary70 = Color(red = 182, green = 157, blue = 248)
+val Primary80 = Color(red = 208, green = 188, blue = 255)
+
+val DEFAULT_PRIMARY = Color(red = 208, green = 188, blue = 255)
+val LIGHT_PURPLE = Color(red = 187, green = 134, blue = 252)
+
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Preview.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Preview.kt
new file mode 100644
index 000000000..c43681fb4
--- /dev/null
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Preview.kt
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2024 Vitor Pamplona
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.vitorpamplona.amethyst.ui.theme
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.vitorpamplona.amethyst.model.ThemeType
+import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
+
+@Composable
+fun ThemeComparisonColumn(toPreview: @Composable () -> Unit) {
+ Column {
+ Box {
+ val darkTheme: SharedPreferencesViewModel = viewModel()
+ darkTheme.updateTheme(ThemeType.DARK)
+ AmethystTheme(darkTheme) {
+ Surface(color = MaterialTheme.colorScheme.background) { toPreview() }
+ }
+ }
+
+ Box {
+ val lightTheme: SharedPreferencesViewModel = viewModel()
+ lightTheme.updateTheme(ThemeType.LIGHT)
+ AmethystTheme(lightTheme) {
+ Surface(color = MaterialTheme.colorScheme.background) { toPreview() }
+ }
+ }
+ }
+}
+
+@Composable
+fun ThemeComparisonRow(toPreview: @Composable () -> Unit) {
+ Row {
+ Box(modifier = Modifier.weight(1f)) {
+ val darkTheme: SharedPreferencesViewModel = viewModel()
+ darkTheme.updateTheme(ThemeType.DARK)
+ AmethystTheme(darkTheme) {
+ Surface(color = MaterialTheme.colorScheme.background) { toPreview() }
+ }
+ }
+
+ Box(modifier = Modifier.weight(1f)) {
+ val lightTheme: SharedPreferencesViewModel = viewModel()
+ lightTheme.updateTheme(ThemeType.LIGHT)
+ AmethystTheme(lightTheme) {
+ Surface(color = MaterialTheme.colorScheme.background) { toPreview() }
+ }
+ }
+ }
+}
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt
index 2e45260a1..cba3da2a1 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Shape.kt
@@ -213,7 +213,6 @@ val CashuCardBorders = Modifier.fillMaxWidth().padding(10.dp).clip(shape = Quote
val EditFieldModifier =
Modifier.padding(start = 10.dp, end = 10.dp, bottom = 10.dp, top = 5.dp).fillMaxWidth()
val EditFieldTrailingIconModifier = Modifier.padding(start = 5.dp, end = 0.dp)
-val EditFieldLeadingIconModifier = Modifier.height(32.dp).padding(start = 2.dp)
val ZeroPadding = PaddingValues(0.dp)
val FeedPadding = PaddingValues(top = 10.dp, bottom = 10.dp)
diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt
index 19e0126ae..a4f60bb7b 100644
--- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt
+++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt
@@ -23,19 +23,18 @@ package com.vitorpamplona.amethyst.ui.theme
import android.app.Activity
import android.app.UiModeManager
import android.content.Context
+import android.graphics.Color.red
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
@@ -55,7 +54,6 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat
-import androidx.lifecycle.viewmodel.compose.viewModel
import com.halilibo.richtext.ui.RichTextStyle
import com.halilibo.richtext.ui.resolveDefaults
import com.patrykandpatrick.vico.compose.common.VicoTheme
@@ -68,8 +66,9 @@ private val DarkColorPalette =
primary = Purple200,
secondary = Teal200,
tertiary = Teal200,
- background = Color(red = 0, green = 0, blue = 0),
- surface = Color(red = 0, green = 0, blue = 0),
+ background = Color.Black, // full black theme
+ surface = Color.Black, // full black theme
+ surfaceDim = Color.Black, // full black theme
surfaceVariant = Color(red = 29, green = 26, blue = 34),
)
@@ -78,6 +77,7 @@ private val LightColorPalette =
primary = Purple500,
secondary = Teal200,
tertiary = Teal200,
+ surfaceContainerHighest = Color(red = 236, green = 230, blue = 240),
surfaceVariant = Color(red = 250, green = 245, blue = 252),
)
@@ -273,6 +273,20 @@ val DarkLargeRelayIconModifier =
.size(Size55dp)
.clip(shape = CircleShape)
+val darkLargeProfilePictureModifier =
+ Modifier
+ .width(120.dp)
+ .height(120.dp)
+ .clip(shape = CircleShape)
+ .border(3.dp, DarkColorPalette.background, CircleShape)
+
+val lightLargeProfilePictureModifier =
+ Modifier
+ .width(120.dp)
+ .height(120.dp)
+ .clip(shape = CircleShape)
+ .border(3.dp, LightColorPalette.background, CircleShape)
+
val RichTextDefaults = RichTextStyle().resolveDefaults()
val MarkDownStyleOnDark =
@@ -447,6 +461,9 @@ val ColorScheme.largeRelayIconModifier: Modifier
val ColorScheme.selectedReactionBoxModifier: Modifier
get() = if (isLight) LightSelectedReactionBoxModifier else DarkSelectedReactionBoxModifier
+val ColorScheme.largeProfilePictureModifier: Modifier
+ get() = if (isLight) lightLargeProfilePictureModifier else darkLargeProfilePictureModifier
+
val chartLightColors =
VicoTheme(
candlestickCartesianLayerColors =
@@ -521,45 +538,3 @@ fun AmethystTheme(
}
}
}
-
-@Composable
-fun ThemeComparisonColumn(toPreview: @Composable () -> Unit) {
- Column {
- Box {
- val darkTheme: SharedPreferencesViewModel = viewModel()
- darkTheme.updateTheme(ThemeType.DARK)
- AmethystTheme(darkTheme) {
- Surface(color = MaterialTheme.colorScheme.background) { toPreview() }
- }
- }
-
- Box {
- val lightTheme: SharedPreferencesViewModel = viewModel()
- lightTheme.updateTheme(ThemeType.LIGHT)
- AmethystTheme(lightTheme) {
- Surface(color = MaterialTheme.colorScheme.background) { toPreview() }
- }
- }
- }
-}
-
-@Composable
-fun ThemeComparisonRow(toPreview: @Composable () -> Unit) {
- Row {
- Box(modifier = Modifier.weight(1f)) {
- val darkTheme: SharedPreferencesViewModel = viewModel()
- darkTheme.updateTheme(ThemeType.DARK)
- AmethystTheme(darkTheme) {
- Surface(color = MaterialTheme.colorScheme.background) { toPreview() }
- }
- }
-
- Box(modifier = Modifier.weight(1f)) {
- val lightTheme: SharedPreferencesViewModel = viewModel()
- lightTheme.updateTheme(ThemeType.LIGHT)
- AmethystTheme(lightTheme) {
- Surface(color = MaterialTheme.colorScheme.background) { toPreview() }
- }
- }
- }
-}
diff --git a/amethyst/src/main/res/values-cs/strings.xml b/amethyst/src/main/res/values-cs/strings.xml
index 336a81b44..1da6feca0 100644
--- a/amethyst/src/main/res/values-cs/strings.xml
+++ b/amethyst/src/main/res/values-cs/strings.xml
@@ -212,6 +212,13 @@
Kanál vytvořen
"Informace kanálu změněna na"
Veřejný chat
+ Metadata veřejného chatu
+ Veřejné konverzace jsou viditelné pro všechny na Nostru a kdokoli
+ se na nich může podílet. Jsou skvělé pro otevřené komunity kolem konkrétních témat.
+ Moderaci lze ovládat smazáním příspěvků na relé
+ Rele
+ Vložte mezi 1-3 relé, které hostí tuto skupinu.
+ Klienti Nostr používají toto nastavení, aby věděli, kam stahovat zprávy a odesílat zprávy.
příspěvků přijato
Odebrat
Automaticky
diff --git a/amethyst/src/main/res/values-de/strings.xml b/amethyst/src/main/res/values-de/strings.xml
index 7914be591..5e2dee8be 100644
--- a/amethyst/src/main/res/values-de/strings.xml
+++ b/amethyst/src/main/res/values-de/strings.xml
@@ -216,6 +216,13 @@ anz der Bedingungen ist erforderlich
Kanal erstellt
"Kanalinformationen geändert in"
Öffentlicher Chat
+ Öffentliche Chat Metadaten
+ Öffentliche Chats sind für jeden auf Nostr sichtbar und jeder
+ kann daran teilnehmen. Sie eignen sich hervorragend für offene Gemeinschaften rund um bestimmte Themen.
+ Moderation kann durch Löschen von Beiträgen auf den Relais gesteuert werden
+ Relais
+ Fügen Sie zwischen 1-3 Relais ein, die diese Gruppe beherbergen.
+ Nostr Clients verwenden diese Einstellung, um zu wissen, woher sie Nachrichten herunterladen und an welche gesendet werden sollen.
empfangene Beiträge
Entfernen
Automatisch
diff --git a/amethyst/src/main/res/values-hu/strings.xml b/amethyst/src/main/res/values-hu/strings.xml
index afec08f2f..35fe3141a 100644
--- a/amethyst/src/main/res/values-hu/strings.xml
+++ b/amethyst/src/main/res/values-hu/strings.xml
@@ -212,6 +212,13 @@
Csatorna létrehozva
"A csatornainformáció a következőre módosult:"
Nyilvános csevegés
+ Nyilvános csevegés metaadatai
+ A nyilvános csevegések mindenki számára láthatóak a Nostr-on, és bárki
+ részt vehet bennük. Ezek kiválóan alkalmasak bizonyos témák köré szerveződő nyílt közösségek számára.
+ A moderálás az átjátszókon lévő hozzászólások törlésével szabályozható
+ Átjátszók
+ Adjon hozzá 1-3 olyan átjátszót, amelyek kiszolgálják ezt a csoportot.
+ A Nostr-kliensek ezt a beállítást használják arra, hogy tudják, honnan töltsék le az üzeneteket, és hová küldjék az üzeneteket.
fogadott bejegyzések
Eltávolítás
Automatikus
@@ -865,6 +872,7 @@
Változások összefoglalása
Gyors javítások…
Javaslat elfogadása
+ Videó lejátszása felugró ablakban
Letöltés
Feliratozás bekapcsolva
Feliratozás kikapcsolva
diff --git a/amethyst/src/main/res/values-pl-rPL/strings.xml b/amethyst/src/main/res/values-pl-rPL/strings.xml
index 265f04fef..9f7fbaf0f 100644
--- a/amethyst/src/main/res/values-pl-rPL/strings.xml
+++ b/amethyst/src/main/res/values-pl-rPL/strings.xml
@@ -205,6 +205,7 @@
z opisem
i zdjęcie
zmieniono nazwę czatu na
+ Nowy profil czatu:
opis dla
i zdjęcie do
Wyjdź
@@ -212,6 +213,10 @@
Kanał utworzony
"Informacje o kanale zmienione na"
Czat Publiczny
+ Metadane Czatu Publicznego
+ Czaty publiczne są widoczne dla wszystkich użytkowników Nostr i każdy może w nich uczestniczyć. Są idealne dla otwartych społeczności skupionych wokół konkretnych tematów. Moderację można kontrolować, usuwając posty z transmiterów
+ Transmitery
+ Wstaw od 1 do 3 transmiterów, które obsługują tę grupę. Klienci Nostr używają tego ustawienia, aby wiedzieć, skąd pobierać wiadomości i do kogo je wysyłać.
odebranych wiadomości
Usuń
Automatycznie
@@ -418,7 +423,7 @@
Port Socks Orbota
Aktywny silnik Tor
Użyj wersji wewnętrznej lub Orbota
- TOR wstępne ustawienia prywatności
+ Tor wstępne ustawienia prywatności
Szybka modyfikacja wszystkich ustawień poniżej
Adres Onion /Transmitery
Użyj Tor dla dowolnego adresu .onion
@@ -519,6 +524,7 @@
Wyślij do Zap Wallet
Otwórz w Portfelu Cashu
Skopiuj token
+ Otwórz w innej aplikacji
Nie ustawiono adresu Lightning
Skopiowano token do schowka
NA ŻYWO
@@ -865,6 +871,7 @@
Podsumowanie zmian
Szybkie poprawki…
Zaakceptuj sugestię
+ Uruchom wideo w pop-upie
Pobierz
Tekst włączony
Tekst wyłączony
diff --git a/amethyst/src/main/res/values-pt-rBR/strings.xml b/amethyst/src/main/res/values-pt-rBR/strings.xml
index b8d043a8d..3c46fdfa8 100644
--- a/amethyst/src/main/res/values-pt-rBR/strings.xml
+++ b/amethyst/src/main/res/values-pt-rBR/strings.xml
@@ -212,6 +212,13 @@
Canal criado
"Informação do canal mudou para"
Chat público
+ Metadados do Chat Público
+ Os chats públicos são visíveis para todos no Nostr, e qualquer pessoa
+ pode participar deles. Eles são ótimos para comunidades abertas em torno de tópicos específicos.
+ A moderação pode ser controlada excluindo postagens em seus relays
+ Relés
+ Inserir entre 1-3 relés que hospedam esse grupo.
+ os clientes Nostr usam essa configuração para saber de onde fazer o download de mensagens e enviar suas mensagens.
postagens recebidas
Remover
Automaticamente
diff --git a/amethyst/src/main/res/values-sv-rSE/strings.xml b/amethyst/src/main/res/values-sv-rSE/strings.xml
index 40eef6e3c..cf72cefd6 100644
--- a/amethyst/src/main/res/values-sv-rSE/strings.xml
+++ b/amethyst/src/main/res/values-sv-rSE/strings.xml
@@ -212,6 +212,13 @@
Kanal skapad
"Kanalinformation ändrad till"
Publik Chat
+ Metadata för offentlig chatt
+ Offentliga chattar är synliga för alla på Nostr och alla
+ kan delta på dem. De är bra för öppna samhällen kring specifika ämnen.
+ Moderation kan kontrolleras genom att ta bort inlägg på reläerna
+ Reläer
+ Infoga mellan 1-3 reläer som är värd för denna grupp.
+ Nostr klienter använder denna inställning för att veta var du kan ladda ner meddelanden från och skicka dina meddelanden till.
mottagna inlägg
Ta bort
Automatiskt
diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml
index 5f73c53be..187985cdf 100644
--- a/amethyst/src/main/res/values/strings.xml
+++ b/amethyst/src/main/res/values/strings.xml
@@ -213,6 +213,7 @@
with description of
and picture
changed chat name to
+ New chat profile:
description to
and picture to
Leave
@@ -220,6 +221,15 @@
Channel created
"Channel Information changed to"
Public Chat
+ Public Chat Metadata
+ Public chats are visible to everyone on Nostr and anyone
+ can participate on them. They are great for open communities around specific topics.
+ Moderation can be controlled by deleting posts on the its relays
+
+ Relays
+ Insert between 1-3 relays that host this group.
+ Nostr clients use this setting to know where to download messages from and send your messages to.
+
posts received
Remove
sats
@@ -607,6 +617,8 @@
Open in Cashu Wallet
Copy Token
+ Open in Another App
+
No Lightning Address set
Copied token to clipboard
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 11b4ace86..23ca61aba 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -16,11 +16,11 @@ coil = "3.1.0"
composeBom = "2025.03.00"
coreKtx = "1.15.0"
espressoCore = "3.6.1"
-firebaseBom = "33.10.0"
+firebaseBom = "33.11.0"
fragmentKtx = "1.8.6"
gms = "4.4.2"
-jacksonModuleKotlin = "2.18.2"
-jna = "5.16.0"
+jacksonModuleKotlin = "2.18.3"
+jna = "5.17.0"
jtorctl = "0.4.5.7"
junit = "4.13.2"
kotlin = "2.1.0"
@@ -46,9 +46,9 @@ torAndroid = "0.4.8.12"
translate = "17.0.3"
unifiedpush = "2.3.1"
urlDetector = "0.1.23"
-vico-charts = "2.0.3"
+vico-charts = "2.1.1"
zelory = "3.0.1"
-zoomable = "2.3.0"
+zoomable = "2.4.0"
zxing = "3.5.3"
zxingAndroidEmbedded = "4.3.0"
windowCoreAndroid = "1.3.0"
diff --git a/quartz/build.gradle b/quartz/build.gradle
index ef440c1a9..12e9db7ed 100644
--- a/quartz/build.gradle
+++ b/quartz/build.gradle
@@ -59,7 +59,7 @@ dependencies {
// LibSodium for ChaCha encryption (NIP-44)
// Wait for @aar support in version catalogs
implementation "com.goterl:lazysodium-android:5.1.0@aar"
- implementation 'net.java.dev.jna:jna:5.16.0@aar'
+ implementation 'net.java.dev.jna:jna:5.17.0@aar'
//implementation (libs.lazysodium.android) { artifact { type = "aar" } }
//implementation (libs.jna) { artifact { type = "aar" } }