Improves the rendering of the channel metadata changes
Redesigns the expandable top bar for NIP-28 chats
This commit is contained in:
@@ -33,9 +33,13 @@ import com.vitorpamplona.ammolite.relays.RelayBriefInfoCache
|
|||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
import com.vitorpamplona.quartz.nip01Core.tags.addressables.ATag
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
|
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.NAddress
|
||||||
|
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.toNEvent
|
import com.vitorpamplona.quartz.nip19Bech32.toNEvent
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
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.nip28PublicChat.base.ChannelData
|
||||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||||
import com.vitorpamplona.quartz.utils.Hex
|
import com.vitorpamplona.quartz.utils.Hex
|
||||||
@@ -46,17 +50,30 @@ class PublicChatChannel(
|
|||||||
idHex: String,
|
idHex: String,
|
||||||
) : Channel(idHex) {
|
) : Channel(idHex) {
|
||||||
var event: ChannelCreateEvent? = null
|
var event: ChannelCreateEvent? = null
|
||||||
|
var infoTags = EmptyTagList
|
||||||
var info = ChannelData(null, null, null, null)
|
var info = ChannelData(null, null, null, null)
|
||||||
|
|
||||||
override fun relays() = info.relays ?: super.relays()
|
override fun relays() = info.relays ?: super.relays()
|
||||||
|
|
||||||
|
fun toNEvent() = NEvent.create(idHex, event?.pubKey, ChannelCreateEvent.KIND, *relays().toTypedArray())
|
||||||
|
|
||||||
|
fun toNostrUri() = "nostr:${toNEvent()}"
|
||||||
|
|
||||||
fun updateChannelInfo(
|
fun updateChannelInfo(
|
||||||
creator: User,
|
creator: User,
|
||||||
channelInfo: ChannelCreateEvent,
|
event: ChannelCreateEvent,
|
||||||
updatedAt: Long,
|
|
||||||
) {
|
) {
|
||||||
this.event = channelInfo
|
this.event = event
|
||||||
updateChannelInfo(creator, channelInfo.channelInfo(), updatedAt)
|
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(
|
fun updateChannelInfo(
|
||||||
@@ -116,9 +133,11 @@ class LiveActivitiesChannel(
|
|||||||
.filter { it.contains(prefix, true) }
|
.filter { it.contains(prefix, true) }
|
||||||
.isNotEmpty()
|
.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 toATag() = ATag(address, relayHintUrl())
|
||||||
|
|
||||||
|
fun toNostrUri() = "nostr:${toNAddr()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Counter(
|
data class Counter(
|
||||||
|
|||||||
@@ -1385,7 +1385,7 @@ object LocalCache {
|
|||||||
}
|
}
|
||||||
if (oldChannel.creator == null || oldChannel.creator == author) {
|
if (oldChannel.creator == null || oldChannel.creator == author) {
|
||||||
if (oldChannel is PublicChatChannel) {
|
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)
|
val author = getOrCreateUser(event.pubKey)
|
||||||
if (event.createdAt > oldChannel.updatedMetadataAt) {
|
if (event.createdAt > oldChannel.updatedMetadataAt) {
|
||||||
if (oldChannel is PublicChatChannel) {
|
if (oldChannel is PublicChatChannel) {
|
||||||
oldChannel.updateChannelInfo(author, event.channelInfo(), event.createdAt)
|
oldChannel.updateChannelInfo(author, event)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()}
|
// Log.d("MT","Relay sent a previous Metadata Event ${oldUser.toBestDisplayName()}
|
||||||
|
|||||||
+2
-6
@@ -21,7 +21,6 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.navigation
|
package com.vitorpamplona.amethyst.ui.navigation
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
@@ -30,6 +29,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -90,11 +90,7 @@ fun MyExtensibleTopAppBar(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (expanded.value && extendableRow != null) {
|
if (expanded.value && extendableRow != null) {
|
||||||
Row(
|
Surface(modifier = Modifier.fillMaxWidth(), tonalElevation = 10.dp) {
|
||||||
Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.Start,
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
) {
|
|
||||||
Column { extendableRow() }
|
Column { extendableRow() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-26
@@ -22,15 +22,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import com.vitorpamplona.amethyst.R
|
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
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.navigation.INav
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.quartz.nip02FollowList.toImmutableListOfLists
|
||||||
import com.vitorpamplona.quartz.nip02FollowList.EmptyTagList
|
|
||||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -41,27 +38,19 @@ fun RenderChangeChannelMetadataNote(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val noteEvent = note.event as? ChannelMetadataEvent ?: return
|
val noteEvent = note.event as? ChannelMetadataEvent ?: return
|
||||||
|
val channelInfo = remember(noteEvent) { noteEvent.channelInfo() }
|
||||||
|
val tags =
|
||||||
|
remember(noteEvent) {
|
||||||
|
noteEvent.tags.toImmutableListOfLists()
|
||||||
|
}
|
||||||
|
|
||||||
val channelInfo = noteEvent.channelInfo()
|
RenderChannelData(
|
||||||
val text =
|
noteEvent.id,
|
||||||
note.author?.toBestDisplayName().toString() +
|
note.toNostrUri(),
|
||||||
" ${stringRes(R.string.changed_chat_name_to)} '" +
|
channelInfo,
|
||||||
(channelInfo.name ?: "") +
|
tags,
|
||||||
"', ${stringRes(R.string.description_to)} '" +
|
bgColor,
|
||||||
(channelInfo.about ?: "") +
|
accountViewModel,
|
||||||
"' ${stringRes(R.string.and_picture_to)} " +
|
nav,
|
||||||
(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,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+310
-21
@@ -20,19 +20,63 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.feed.types
|
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.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
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.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
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.R
|
||||||
|
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
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.components.TranslatableRichTextViewer
|
||||||
|
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
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.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.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.admin.ChannelCreateEvent
|
||||||
|
import com.vitorpamplona.quartz.nip28PublicChat.base.ChannelData
|
||||||
|
import com.vitorpamplona.quartz.nip65RelayList.RelayUrlFormatter
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RenderCreateChannelNote(
|
fun RenderCreateChannelNote(
|
||||||
@@ -42,27 +86,272 @@ fun RenderCreateChannelNote(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val noteEvent = note.event as? ChannelCreateEvent ?: return
|
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 =
|
RenderChannelData(
|
||||||
note.author?.toBestDisplayName().toString() +
|
noteEvent.id,
|
||||||
" ${stringRes(R.string.created)} " +
|
note.toNostrUri(),
|
||||||
(channelInfo.name ?: "") +
|
channelInfo,
|
||||||
" ${stringRes(R.string.with_description_of)} '" +
|
tags,
|
||||||
(channelInfo.about ?: "") +
|
bgColor,
|
||||||
"' ${stringRes(R.string.and_picture_to)} " +
|
accountViewModel,
|
||||||
(channelInfo.picture ?: "")
|
nav,
|
||||||
|
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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<String>,
|
||||||
|
bgColor: MutableState<Color>,
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
-120
@@ -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, nav)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
+106
-6
@@ -20,25 +20,35 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header
|
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.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
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.R
|
||||||
|
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||||
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
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.LoadNote
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
|
||||||
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||||
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
|
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.MoreOptionsButton
|
||||||
import com.vitorpamplona.amethyst.ui.note.elements.NormalTimeAgo
|
import com.vitorpamplona.amethyst.ui.note.elements.NormalTimeAgo
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
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.stringRes
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size25dp
|
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
|
@Composable
|
||||||
fun LongPublicChatChannelHeader(
|
fun LongPublicChatChannelHeader(
|
||||||
baseChannel: PublicChatChannel,
|
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,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val channelState by baseChannel.live.observeAsState()
|
val channelState by baseChannel.live.observeAsState()
|
||||||
val channel = channelState?.channel as? PublicChatChannel ?: return
|
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) {
|
Row(lineModifier) {
|
||||||
val summary = remember(channelState) { channel.summary()?.ifBlank { null } }
|
val summary = remember(channelState) { channel.summary()?.ifBlank { null } }
|
||||||
|
|
||||||
@@ -73,7 +126,7 @@ fun LongPublicChatChannelHeader(
|
|||||||
content = summary ?: stringRes(id = R.string.groups_no_descriptor),
|
content = summary ?: stringRes(id = R.string.groups_no_descriptor),
|
||||||
canPreview = false,
|
canPreview = false,
|
||||||
quotesLeft = 1,
|
quotesLeft = 1,
|
||||||
tags = EmptyTagList,
|
tags = channel.infoTags,
|
||||||
backgroundColor = background,
|
backgroundColor = background,
|
||||||
id = baseChannel.idHex,
|
id = baseChannel.idHex,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
@@ -81,9 +134,6 @@ fun LongPublicChatChannelHeader(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(DoubleHorzSpacer)
|
|
||||||
LongChannelActionOptions(channel, accountViewModel, nav)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadNote(baseNoteHex = channel.idHex, accountViewModel) { loadingNote ->
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
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.INav
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.TopBarExtensibleWithBackButton
|
import com.vitorpamplona.amethyst.ui.navigation.TopBarExtensibleWithBackButton
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
|
|||||||
+41
@@ -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.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -38,12 +40,18 @@ import androidx.compose.ui.unit.dp
|
|||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.FeatureSetType
|
import com.vitorpamplona.amethyst.model.FeatureSetType
|
||||||
import com.vitorpamplona.amethyst.model.PublicChatChannel
|
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.components.RobohashFallbackAsyncImage
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
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.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.stringRes
|
||||||
import com.vitorpamplona.amethyst.ui.theme.HeaderPictureModifier
|
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.Size35dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ShortPublicChatChannelHeader(
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+2
-2
@@ -24,7 +24,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.EditNote
|
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.material3.Icon
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -46,7 +46,7 @@ fun EditButton(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
Button(
|
FilledTonalButton(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(horizontal = 3.dp)
|
.padding(horizontal = 3.dp)
|
||||||
|
|||||||
+2
-2
@@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions
|
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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -38,7 +38,7 @@ fun JoinChatButton(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
Button(
|
FilledTonalButton(
|
||||||
modifier = HalfHalfHorzModifier,
|
modifier = HalfHalfHorzModifier,
|
||||||
onClick = { accountViewModel.follow(channel) },
|
onClick = { accountViewModel.follow(channel) },
|
||||||
contentPadding = ButtonPadding,
|
contentPadding = ButtonPadding,
|
||||||
|
|||||||
+2
-2
@@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.nip28PublicChat.header.actions
|
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.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -38,7 +38,7 @@ fun LeaveChatButton(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
Button(
|
FilledTonalButton(
|
||||||
modifier = HalfHalfHorzModifier,
|
modifier = HalfHalfHorzModifier,
|
||||||
onClick = { accountViewModel.unfollow(channel) },
|
onClick = { accountViewModel.unfollow(channel) },
|
||||||
contentPadding = ButtonPadding,
|
contentPadding = ButtonPadding,
|
||||||
|
|||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
* 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.graphics.Color
|
||||||
|
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(
|
||||||
|
tint = Color.White,
|
||||||
|
imageVector = Icons.Default.ContentCopy,
|
||||||
|
contentDescription = stringRes(R.string.quick_action_copy_note_id),
|
||||||
|
modifier = Size20Modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+74
@@ -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.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.graphics.Color
|
||||||
|
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(
|
||||||
|
tint = Color.White,
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
|
||||||
|
contentDescription = stringRes(R.string.quick_actions_open_in_another_app),
|
||||||
|
modifier = Size20Modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* 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.graphics.Color
|
||||||
|
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(
|
||||||
|
tint = Color.White,
|
||||||
|
imageVector = Icons.Default.Share,
|
||||||
|
contentDescription = stringRes(R.string.quick_action_share),
|
||||||
|
modifier = Size20Modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-10
@@ -20,7 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode
|
||||||
|
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -28,8 +27,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
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.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -43,7 +40,6 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
@@ -65,6 +61,7 @@ import com.vitorpamplona.amethyst.ui.stringRes
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
import com.vitorpamplona.amethyst.ui.theme.Font14SP
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
import com.vitorpamplona.amethyst.ui.theme.Size35dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.largeProfilePictureModifier
|
||||||
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
|
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@@ -135,12 +132,7 @@ fun ShowQRDialog(
|
|||||||
robot = user.pubkeyHex,
|
robot = user.pubkeyHex,
|
||||||
model = user.profilePicture(),
|
model = user.profilePicture(),
|
||||||
contentDescription = stringRes(R.string.profile_image),
|
contentDescription = stringRes(R.string.profile_image),
|
||||||
modifier =
|
modifier = MaterialTheme.colorScheme.largeProfilePictureModifier,
|
||||||
Modifier
|
|
||||||
.width(120.dp)
|
|
||||||
.height(120.dp)
|
|
||||||
.clip(shape = CircleShape)
|
|
||||||
.border(3.dp, MaterialTheme.colorScheme.onBackground, CircleShape),
|
|
||||||
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
|
||||||
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ import androidx.compose.foundation.layout.Box
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -273,6 +275,20 @@ val DarkLargeRelayIconModifier =
|
|||||||
.size(Size55dp)
|
.size(Size55dp)
|
||||||
.clip(shape = CircleShape)
|
.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 RichTextDefaults = RichTextStyle().resolveDefaults()
|
||||||
|
|
||||||
val MarkDownStyleOnDark =
|
val MarkDownStyleOnDark =
|
||||||
@@ -447,6 +463,9 @@ val ColorScheme.largeRelayIconModifier: Modifier
|
|||||||
val ColorScheme.selectedReactionBoxModifier: Modifier
|
val ColorScheme.selectedReactionBoxModifier: Modifier
|
||||||
get() = if (isLight) LightSelectedReactionBoxModifier else DarkSelectedReactionBoxModifier
|
get() = if (isLight) LightSelectedReactionBoxModifier else DarkSelectedReactionBoxModifier
|
||||||
|
|
||||||
|
val ColorScheme.largeProfilePictureModifier: Modifier
|
||||||
|
get() = if (isLight) lightLargeProfilePictureModifier else darkLargeProfilePictureModifier
|
||||||
|
|
||||||
val chartLightColors =
|
val chartLightColors =
|
||||||
VicoTheme(
|
VicoTheme(
|
||||||
candlestickCartesianLayerColors =
|
candlestickCartesianLayerColors =
|
||||||
|
|||||||
@@ -213,6 +213,7 @@
|
|||||||
<string name="with_description_of">with description of</string>
|
<string name="with_description_of">with description of</string>
|
||||||
<string name="and_picture">and picture</string>
|
<string name="and_picture">and picture</string>
|
||||||
<string name="changed_chat_name_to">changed chat name to</string>
|
<string name="changed_chat_name_to">changed chat name to</string>
|
||||||
|
<string name="changed_chat_profile_to">New chat profile:</string>
|
||||||
<string name="description_to">description to</string>
|
<string name="description_to">description to</string>
|
||||||
<string name="and_picture_to">and picture to</string>
|
<string name="and_picture_to">and picture to</string>
|
||||||
<string name="leave">Leave</string>
|
<string name="leave">Leave</string>
|
||||||
@@ -616,6 +617,8 @@
|
|||||||
<string name="cashu_redeem_to_cashu">Open in Cashu Wallet</string>
|
<string name="cashu_redeem_to_cashu">Open in Cashu Wallet</string>
|
||||||
<string name="cashu_copy_token">Copy Token</string>
|
<string name="cashu_copy_token">Copy Token</string>
|
||||||
|
|
||||||
|
<string name="quick_actions_open_in_another_app">Open in Another App</string>
|
||||||
|
|
||||||
<string name="no_lightning_address_set">No Lightning Address set</string>
|
<string name="no_lightning_address_set">No Lightning Address set</string>
|
||||||
<string name="copied_token_to_clipboard">Copied token to clipboard</string>
|
<string name="copied_token_to_clipboard">Copied token to clipboard</string>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user