feat: modernize Git Repository, Issue, and Patch card design
Replace plain text layout in NoteCompose Git renderers with a unified card style: icon badges, type chips, a compact repository header pill, and icon-led link rows for web/clone URLs. Patches now surface the short commit hash in the header.
This commit is contained in:
@@ -20,13 +20,18 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.note.types
|
package com.vitorpamplona.amethyst.ui.note.types
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
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.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
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
|
||||||
@@ -34,12 +39,18 @@ import androidx.compose.runtime.MutableState
|
|||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
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.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
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.dp
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
|
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||||
|
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol
|
||||||
|
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||||
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
|
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
|
||||||
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
import com.vitorpamplona.amethyst.commons.model.toImmutableListOfLists
|
||||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
import com.vitorpamplona.amethyst.model.AddressableNote
|
||||||
@@ -54,14 +65,16 @@ import com.vitorpamplona.amethyst.ui.note.LoadDecryptedContent
|
|||||||
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
import com.vitorpamplona.amethyst.ui.note.elements.DisplayUncitedHashtags
|
||||||
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.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
|
import com.vitorpamplona.amethyst.ui.theme.Font12SP
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.HalfDoubleVertSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
import com.vitorpamplona.amethyst.ui.theme.Size10dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.Size16dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.Size5dp
|
import com.vitorpamplona.amethyst.ui.theme.Size5dp
|
||||||
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
|
import com.vitorpamplona.amethyst.ui.theme.Size8dp
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.grayText
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import com.vitorpamplona.amethyst.ui.theme.replyModifier
|
|
||||||
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
|
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
|
||||||
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
|
import com.vitorpamplona.quartz.nip01Core.tags.hashtags.hasHashtags
|
||||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||||
@@ -70,6 +83,101 @@ import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent
|
|||||||
import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
|
import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
|
||||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||||
|
|
||||||
|
private val CardShape = QuoteBorder
|
||||||
|
private val ChipShape = RoundedCornerShape(8.dp)
|
||||||
|
private val CardPadding = PaddingValues(Size10dp)
|
||||||
|
private val HeaderSpacing = Arrangement.spacedBy(Size8dp)
|
||||||
|
private val LinkRowSpacing = Arrangement.spacedBy(Size8dp)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun GitCardContainer(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
val border = MaterialTheme.colorScheme.subtleBorder
|
||||||
|
Column(
|
||||||
|
modifier =
|
||||||
|
modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(CardShape)
|
||||||
|
.border(1.dp, border, CardShape)
|
||||||
|
.padding(CardPadding),
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun IconBadge(
|
||||||
|
symbol: MaterialSymbol,
|
||||||
|
contentDescription: String?,
|
||||||
|
background: Color,
|
||||||
|
tint: Color,
|
||||||
|
size: Dp = 28.dp,
|
||||||
|
iconSize: Dp = Size16dp,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.size(size)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(background),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
symbol = symbol,
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.size(iconSize),
|
||||||
|
tint = tint,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TypeChip(
|
||||||
|
text: String,
|
||||||
|
background: Color,
|
||||||
|
contentColor: Color,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
color = contentColor,
|
||||||
|
maxLines = 1,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.clip(ChipShape)
|
||||||
|
.background(background)
|
||||||
|
.padding(horizontal = Size8dp, vertical = 2.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun LinkRow(
|
||||||
|
symbol: MaterialSymbol,
|
||||||
|
contentDescription: String?,
|
||||||
|
url: String,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = LinkRowSpacing,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
symbol = symbol,
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.size(Size16dp),
|
||||||
|
tint = MaterialTheme.colorScheme.grayText,
|
||||||
|
)
|
||||||
|
ClickableUrl(
|
||||||
|
url = url,
|
||||||
|
urlText = url.removePrefix("https://").removePrefix("http://"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RenderGitPatchEvent(
|
fun RenderGitPatchEvent(
|
||||||
baseNote: Note,
|
baseNote: Note,
|
||||||
@@ -101,26 +209,45 @@ private fun RenderShortRepositoryHeader(
|
|||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val noteEvent by observeNoteEvent<GitRepositoryEvent>(baseNote, accountViewModel)
|
val noteEvent by observeNoteEvent<GitRepositoryEvent>(baseNote, accountViewModel)
|
||||||
|
val title = noteEvent?.name() ?: baseNote.dTag()
|
||||||
|
val summary = noteEvent?.description()
|
||||||
|
|
||||||
Column(
|
Row(
|
||||||
modifier = MaterialTheme.colorScheme.replyModifier.padding(10.dp),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(ChipShape)
|
||||||
|
.background(MaterialTheme.colorScheme.onSurface.copy(alpha = 0.04f))
|
||||||
|
.padding(horizontal = Size10dp, vertical = Size8dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = HeaderSpacing,
|
||||||
) {
|
) {
|
||||||
val title = noteEvent?.name() ?: baseNote.dTag()
|
IconBadge(
|
||||||
Text(
|
symbol = MaterialSymbols.Code,
|
||||||
text = stringRes(id = R.string.git_repository, title),
|
contentDescription = null,
|
||||||
style = MaterialTheme.typography.titleLarge,
|
background = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f),
|
||||||
maxLines = 1,
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
overflow = TextOverflow.Ellipsis,
|
size = 24.dp,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
iconSize = 14.dp,
|
||||||
)
|
)
|
||||||
|
|
||||||
noteEvent?.description()?.let {
|
Column(modifier = Modifier.weight(1f, fill = true)) {
|
||||||
Spacer(modifier = DoubleVertSpacer)
|
|
||||||
Text(
|
Text(
|
||||||
text = it,
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
|
summary?.let {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.grayText,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,72 +263,96 @@ private fun RenderGitPatchEvent(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val repository = remember(noteEvent) { noteEvent.repositoryAddress() }
|
GitCardContainer {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = HeaderSpacing,
|
||||||
|
) {
|
||||||
|
TypeChip(
|
||||||
|
text = stringRes(id = R.string.kind_git_patch),
|
||||||
|
background = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.15f),
|
||||||
|
contentColor = MaterialTheme.colorScheme.tertiary,
|
||||||
|
)
|
||||||
|
|
||||||
if (repository != null) {
|
val commit = remember(noteEvent) { noteEvent.commit() }
|
||||||
LoadAddressableNote(repository, accountViewModel) {
|
commit?.takeIf { it.isNotBlank() }?.let { hash ->
|
||||||
if (it != null) {
|
Text(
|
||||||
RenderShortRepositoryHeader(it, accountViewModel, nav)
|
text = hash.take(7),
|
||||||
Spacer(modifier = DoubleVertSpacer)
|
style = MaterialTheme.typography.labelMedium.copy(fontSize = Font12SP),
|
||||||
|
color = MaterialTheme.colorScheme.grayText,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
LoadDecryptedContent(note, accountViewModel) { body ->
|
val repository = remember(noteEvent) { noteEvent.repositoryAddress() }
|
||||||
val eventContent by
|
if (repository != null) {
|
||||||
remember(note.event) {
|
Spacer(modifier = StdVertSpacer)
|
||||||
derivedStateOf {
|
LoadAddressableNote(repository, accountViewModel) {
|
||||||
val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null }
|
if (it != null) {
|
||||||
|
RenderShortRepositoryHeader(it, accountViewModel, nav)
|
||||||
if (!subject.isNullOrBlank() && !body.split("\n")[0].contains(subject)) {
|
|
||||||
"### $subject\n$body"
|
|
||||||
} else {
|
|
||||||
body
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val isAuthorTheLoggedUser =
|
Spacer(modifier = HalfDoubleVertSpacer)
|
||||||
remember(note.event) { accountViewModel.isLoggedUser(note.author) }
|
|
||||||
|
|
||||||
if (makeItShort && isAuthorTheLoggedUser) {
|
LoadDecryptedContent(note, accountViewModel) { body ->
|
||||||
Text(
|
val eventContent by
|
||||||
text = eventContent,
|
remember(note.event) {
|
||||||
color = MaterialTheme.colorScheme.placeholderText,
|
derivedStateOf {
|
||||||
maxLines = 2,
|
val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null }
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
val callbackUri = remember(note) { note.toNostrUri() }
|
|
||||||
|
|
||||||
SensitivityWarning(
|
if (!subject.isNullOrBlank() && !body.split("\n")[0].contains(subject)) {
|
||||||
note = note,
|
"### $subject\n$body"
|
||||||
accountViewModel = accountViewModel,
|
} else {
|
||||||
) {
|
body
|
||||||
val tags = remember(note) { note.event?.tags?.toImmutableListOfLists() ?: EmptyTagList }
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TranslatableRichTextViewer(
|
val isAuthorTheLoggedUser =
|
||||||
content = eventContent,
|
remember(note.event) { accountViewModel.isLoggedUser(note.author) }
|
||||||
canPreview = canPreview && !makeItShort,
|
|
||||||
quotesLeft = quotesLeft,
|
if (makeItShort && isAuthorTheLoggedUser) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
Text(
|
||||||
tags = tags,
|
text = eventContent,
|
||||||
backgroundColor = backgroundColor,
|
color = MaterialTheme.colorScheme.placeholderText,
|
||||||
id = note.idHex,
|
maxLines = 2,
|
||||||
callbackUri = callbackUri,
|
overflow = TextOverflow.Ellipsis,
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
nav = nav,
|
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
|
val callbackUri = remember(note) { note.toNostrUri() }
|
||||||
|
|
||||||
if (note.event?.hasHashtags() == true) {
|
SensitivityWarning(
|
||||||
DisplayUncitedHashtags(
|
note = note,
|
||||||
event = noteEvent,
|
|
||||||
content = eventContent,
|
|
||||||
callbackUri = callbackUri,
|
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
) {
|
||||||
)
|
val tags = remember(note) { note.event?.tags?.toImmutableListOfLists() ?: EmptyTagList }
|
||||||
|
|
||||||
|
TranslatableRichTextViewer(
|
||||||
|
content = eventContent,
|
||||||
|
canPreview = canPreview && !makeItShort,
|
||||||
|
quotesLeft = quotesLeft,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
tags = tags,
|
||||||
|
backgroundColor = backgroundColor,
|
||||||
|
id = note.idHex,
|
||||||
|
callbackUri = callbackUri,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note.event?.hasHashtags() == true) {
|
||||||
|
DisplayUncitedHashtags(
|
||||||
|
event = noteEvent,
|
||||||
|
content = eventContent,
|
||||||
|
callbackUri = callbackUri,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,67 +393,76 @@ private fun RenderGitIssueEvent(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val repository = remember(noteEvent) { noteEvent.repositoryAddress() }
|
GitCardContainer {
|
||||||
|
TypeChip(
|
||||||
|
text = stringRes(id = R.string.kind_git_issue),
|
||||||
|
background = MaterialTheme.colorScheme.primary.copy(alpha = 0.15f),
|
||||||
|
contentColor = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
|
||||||
if (repository != null) {
|
val repository = remember(noteEvent) { noteEvent.repositoryAddress() }
|
||||||
LoadAddressableNote(repository, accountViewModel) {
|
if (repository != null) {
|
||||||
if (it != null) {
|
Spacer(modifier = StdVertSpacer)
|
||||||
RenderShortRepositoryHeader(it, accountViewModel, nav)
|
LoadAddressableNote(repository, accountViewModel) {
|
||||||
Spacer(modifier = DoubleVertSpacer)
|
if (it != null) {
|
||||||
}
|
RenderShortRepositoryHeader(it, accountViewModel, nav)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadDecryptedContent(note, accountViewModel) { body ->
|
|
||||||
val eventContent by
|
|
||||||
remember(note.event) {
|
|
||||||
derivedStateOf {
|
|
||||||
val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null }
|
|
||||||
|
|
||||||
if (!subject.isNullOrBlank() && !body.split("\n")[0].contains(subject)) {
|
|
||||||
"### $subject\n$body"
|
|
||||||
} else {
|
|
||||||
body
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val isAuthorTheLoggedUser =
|
Spacer(modifier = HalfDoubleVertSpacer)
|
||||||
remember(note.event) { accountViewModel.isLoggedUser(note.author) }
|
|
||||||
|
|
||||||
if (makeItShort && isAuthorTheLoggedUser) {
|
LoadDecryptedContent(note, accountViewModel) { body ->
|
||||||
Text(
|
val eventContent by
|
||||||
text = eventContent,
|
remember(note.event) {
|
||||||
color = MaterialTheme.colorScheme.placeholderText,
|
derivedStateOf {
|
||||||
maxLines = 2,
|
val subject = (note.event as? TextNoteEvent)?.subject()?.ifEmpty { null }
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
val callbackUri = remember(note) { note.toNostrUri() }
|
|
||||||
|
|
||||||
SensitivityWarning(
|
if (!subject.isNullOrBlank() && !body.split("\n")[0].contains(subject)) {
|
||||||
note = note,
|
"### $subject\n$body"
|
||||||
accountViewModel = accountViewModel,
|
} else {
|
||||||
) {
|
body
|
||||||
val tags =
|
}
|
||||||
remember(note) { note.event?.tags?.toImmutableListOfLists() ?: EmptyTagList }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TranslatableRichTextViewer(
|
val isAuthorTheLoggedUser =
|
||||||
content = eventContent,
|
remember(note.event) { accountViewModel.isLoggedUser(note.author) }
|
||||||
canPreview = canPreview && !makeItShort,
|
|
||||||
quotesLeft = quotesLeft,
|
if (makeItShort && isAuthorTheLoggedUser) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
Text(
|
||||||
tags = tags,
|
text = eventContent,
|
||||||
backgroundColor = backgroundColor,
|
color = MaterialTheme.colorScheme.placeholderText,
|
||||||
id = note.idHex,
|
maxLines = 2,
|
||||||
callbackUri = callbackUri,
|
overflow = TextOverflow.Ellipsis,
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
nav = nav,
|
|
||||||
)
|
)
|
||||||
}
|
} else {
|
||||||
|
val callbackUri = remember(note) { note.toNostrUri() }
|
||||||
|
|
||||||
if (note.event?.hasHashtags() == true) {
|
SensitivityWarning(
|
||||||
DisplayUncitedHashtags(noteEvent, eventContent, callbackUri, accountViewModel, nav)
|
note = note,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
) {
|
||||||
|
val tags =
|
||||||
|
remember(note) { note.event?.tags?.toImmutableListOfLists() ?: EmptyTagList }
|
||||||
|
|
||||||
|
TranslatableRichTextViewer(
|
||||||
|
content = eventContent,
|
||||||
|
canPreview = canPreview && !makeItShort,
|
||||||
|
quotesLeft = quotesLeft,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
tags = tags,
|
||||||
|
backgroundColor = backgroundColor,
|
||||||
|
id = note.idHex,
|
||||||
|
callbackUri = callbackUri,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note.event?.hasHashtags() == true) {
|
||||||
|
DisplayUncitedHashtags(noteEvent, eventContent, callbackUri, accountViewModel, nav)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -331,62 +491,62 @@ private fun RenderGitRepositoryEvent(
|
|||||||
val web = noteEvent.web()
|
val web = noteEvent.web()
|
||||||
val clone = noteEvent.clone()
|
val clone = noteEvent.clone()
|
||||||
|
|
||||||
Row(
|
GitCardContainer {
|
||||||
modifier =
|
Row(
|
||||||
Modifier
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
.clip(shape = QuoteBorder)
|
horizontalArrangement = HeaderSpacing,
|
||||||
.border(
|
modifier = Modifier.fillMaxWidth(),
|
||||||
1.dp,
|
) {
|
||||||
MaterialTheme.colorScheme.subtleBorder,
|
IconBadge(
|
||||||
QuoteBorder,
|
symbol = MaterialSymbols.Code,
|
||||||
).padding(Size10dp),
|
contentDescription = null,
|
||||||
) {
|
background = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f),
|
||||||
Column {
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
Text(
|
|
||||||
text = stringRes(id = R.string.git_repository, title),
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
summary?.let {
|
Column(modifier = Modifier.weight(1f, fill = true)) {
|
||||||
Text(
|
Text(
|
||||||
text = it,
|
text = stringRes(id = R.string.kind_git_repo),
|
||||||
modifier = Modifier.fillMaxWidth().padding(vertical = Size5dp),
|
style = MaterialTheme.typography.labelSmall,
|
||||||
maxLines = 3,
|
color = MaterialTheme.colorScheme.grayText,
|
||||||
|
maxLines = 1,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HorizontalDivider(thickness = DividerThickness)
|
summary?.let {
|
||||||
|
Spacer(modifier = HalfDoubleVertSpacer)
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
maxLines = 4,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
web?.let {
|
if (web != null || clone != null) {
|
||||||
Row(Modifier.fillMaxWidth().padding(top = Size5dp)) {
|
Spacer(modifier = HalfDoubleVertSpacer)
|
||||||
Text(
|
Column(verticalArrangement = Arrangement.spacedBy(Size5dp)) {
|
||||||
text = stringRes(id = R.string.git_web_address),
|
web?.let {
|
||||||
maxLines = 1,
|
LinkRow(
|
||||||
overflow = TextOverflow.Ellipsis,
|
symbol = MaterialSymbols.Public,
|
||||||
)
|
contentDescription = stringRes(id = R.string.git_web_address),
|
||||||
Spacer(modifier = StdHorzSpacer)
|
|
||||||
ClickableUrl(
|
|
||||||
url = it,
|
url = it,
|
||||||
urlText = it.removePrefix("https://").removePrefix("http://"),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
clone?.let {
|
||||||
|
LinkRow(
|
||||||
clone?.let {
|
symbol = MaterialSymbols.AutoMirrored.OpenInNew,
|
||||||
Row(Modifier.fillMaxWidth().padding(top = Size5dp)) {
|
contentDescription = stringRes(id = R.string.git_clone_address),
|
||||||
Text(
|
|
||||||
text = stringRes(id = R.string.git_clone_address),
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
Spacer(modifier = StdHorzSpacer)
|
|
||||||
ClickableUrl(
|
|
||||||
url = it,
|
url = it,
|
||||||
urlText = it.removePrefix("https://").removePrefix("http://"),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user