Initial refactoring of the elements within a post.

This commit is contained in:
Vitor Pamplona
2023-12-22 19:44:01 -05:00
parent 720ebfd0ea
commit bbd8a34f44
23 changed files with 677 additions and 457 deletions
@@ -0,0 +1,93 @@
package com.vitorpamplona.amethyst.ui.elements
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.ThemeComparison
@Composable
@Preview
fun AddButtonPreview() {
ThemeComparison(
onDark = {
Row() {
Column {
AddButton(isActive = true) {}
AddButton(isActive = false) {}
}
Column {
RemoveButton(isActive = true) {}
RemoveButton(isActive = false) {}
}
}
},
onLight = {
Row() {
Column {
AddButton(isActive = true) {}
AddButton(isActive = false) {}
}
Column {
RemoveButton(isActive = true) {}
RemoveButton(isActive = false) {}
}
}
}
)
}
@Composable
fun AddButton(
text: Int = R.string.add,
isActive: Boolean = true,
modifier: Modifier = Modifier.padding(start = 3.dp),
onClick: () -> Unit
) {
Button(
modifier = modifier,
onClick = {
if (isActive) {
onClick()
}
},
shape = ButtonBorder,
enabled = isActive,
contentPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)
) {
Text(text = stringResource(text), color = Color.White, textAlign = TextAlign.Center)
}
}
@Composable
fun RemoveButton(
isActive: Boolean = true,
onClick: () -> Unit
) {
Button(
modifier = Modifier.padding(start = 3.dp),
onClick = {
if (isActive) {
onClick()
}
},
shape = ButtonBorder,
enabled = isActive,
contentPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)
) {
Text(text = stringResource(R.string.remove), color = Color.White)
}
}
@@ -0,0 +1,60 @@
package com.vitorpamplona.amethyst.ui.elements
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.text.AnnotatedString
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
@Composable
fun DisplayFollowingCommunityInPost(
baseNote: Note,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
Column(HalfStartPadding) {
Row(verticalAlignment = Alignment.CenterVertically) {
DisplayCommunity(baseNote, nav)
}
}
}
@Composable
private fun DisplayCommunity(note: Note, nav: (String) -> Unit) {
val communityTag = remember(note) {
note.event?.getTagOfAddressableKind(CommunityDefinitionEvent.kind)
} ?: return
val displayTag = remember(note) { AnnotatedString(getCommunityShortName(communityTag)) }
val route = remember(note) { "Community/${communityTag.toTag()}" }
ClickableText(
text = displayTag,
onClick = { nav(route) },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
),
maxLines = 1
)
}
private fun getCommunityShortName(communityTag: ATag): String {
val name = if (communityTag.dTag.length > 10) {
communityTag.dTag.take(10) + "..."
} else {
communityTag.dTag.take(10)
}
return "/n/$name"
}
@@ -0,0 +1,71 @@
package com.vitorpamplona.amethyst.ui.elements
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.text.AnnotatedString
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun DisplayFollowingHashtagsInPost(
baseNote: Note,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
val noteEvent = remember { baseNote.event } ?: return
val userFollowState by accountViewModel.userFollows.observeAsState()
var firstTag by remember { mutableStateOf<String?>(null) }
LaunchedEffect(key1 = userFollowState) {
launch(Dispatchers.Default) {
val followingTags = userFollowState?.user?.cachedFollowingTagSet() ?: emptySet()
val newFirstTag = noteEvent.firstIsTaggedHashes(followingTags)
if (firstTag != newFirstTag) {
launch(Dispatchers.Main) {
firstTag = newFirstTag
}
}
}
}
firstTag?.let {
Column(verticalArrangement = Arrangement.Center) {
Row(verticalAlignment = Alignment.CenterVertically) {
DisplayTagList(it, nav)
}
}
}
}
@Composable
private fun DisplayTagList(firstTag: String, nav: (String) -> Unit) {
val displayTag = remember(firstTag) { AnnotatedString(" #$firstTag") }
val route = remember(firstTag) { "Hashtag/$firstTag" }
ClickableText(
text = displayTag,
onClick = { nav(route) },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
),
maxLines = 1
)
}
@@ -0,0 +1,41 @@
package com.vitorpamplona.amethyst.ui.elements
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.ThemeComparison
import com.vitorpamplona.amethyst.ui.theme.lessImportantLink
@Composable
@Preview
fun DisplayPoWPreview() {
ThemeComparison(
onDark = {
DisplayPoW(pow = 24)
},
onLight = {
DisplayPoW(pow = 24)
}
)
}
@Composable
fun DisplayPoW(
pow: Int
) {
val powStr = remember(pow) {
"PoW-$pow"
}
Text(
powStr,
color = MaterialTheme.colorScheme.lessImportantLink,
fontSize = Font14SP,
fontWeight = FontWeight.Bold,
maxLines = 1
)
}
@@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.ui.note
package com.vitorpamplona.amethyst.ui.elements
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
@@ -9,19 +10,26 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
@@ -35,10 +43,102 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.actions.CloseButton
import com.vitorpamplona.amethyst.ui.actions.PostButton
import com.vitorpamplona.amethyst.ui.note.ZapIcon
import com.vitorpamplona.amethyst.ui.note.ZappedIcon
import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.math.BigDecimal
@Stable
data class Reward(val amount: BigDecimal)
@Composable
fun DisplayReward(
baseReward: Reward,
baseNote: Note,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
var popupExpanded by remember { mutableStateOf(false) }
Column() {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { popupExpanded = true }
) {
ClickableText(
text = AnnotatedString("#bounty"),
onClick = { nav("Hashtag/bounty") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
)
)
RenderPledgeAmount(baseNote, baseReward, accountViewModel)
}
if (popupExpanded) {
AddBountyAmountDialog(baseNote, accountViewModel) {
popupExpanded = false
}
}
}
}
@Composable
private fun RenderPledgeAmount(
baseNote: Note,
baseReward: Reward,
accountViewModel: AccountViewModel
) {
val repliesState by baseNote.live().replies.observeAsState()
var reward by remember {
mutableStateOf<String>(
showAmount(baseReward.amount)
)
}
var hasPledge by remember {
mutableStateOf<Boolean>(
false
)
}
LaunchedEffect(key1 = repliesState) {
launch(Dispatchers.IO) {
repliesState?.note?.pledgedAmountByOthers()?.let {
val newRewardAmount = showAmount(baseReward.amount.add(it))
if (newRewardAmount != reward) {
reward = newRewardAmount
}
}
val newHasPledge = repliesState?.note?.hasPledgeBy(accountViewModel.userProfile()) == true
if (hasPledge != newHasPledge) {
launch(Dispatchers.Main) {
hasPledge = newHasPledge
}
}
}
}
if (hasPledge) {
ZappedIcon(modifier = Size20Modifier)
} else {
ZapIcon(modifier = Size20Modifier, MaterialTheme.colorScheme.placeholderText)
}
Text(
text = reward,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
}
class AddBountyAmountViewModel : ViewModel() {
private var account: Account? = null
@@ -0,0 +1,41 @@
package com.vitorpamplona.amethyst.ui.elements
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.text.AnnotatedString
import com.vitorpamplona.amethyst.ui.theme.HalfTopPadding
import com.vitorpamplona.amethyst.ui.theme.lessImportantLink
import kotlinx.collections.immutable.ImmutableList
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun DisplayUncitedHashtags(
hashtags: ImmutableList<String>,
eventContent: String,
nav: (String) -> Unit
) {
val unusedHashtags = remember(eventContent) {
hashtags.filter { !eventContent.contains(it, true) }
}
if (unusedHashtags.isNotEmpty()) {
FlowRow(
modifier = HalfTopPadding
) {
unusedHashtags.forEach { hashtag ->
ClickableText(
text = remember { AnnotatedString("#$hashtag ") },
onClick = { nav("Hashtag/$hashtag") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.lessImportantLink
)
)
}
}
}
}
@@ -0,0 +1,84 @@
package com.vitorpamplona.amethyst.ui.elements
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowForwardIos
import androidx.compose.material.icons.outlined.Bolt
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.note.UserPicture
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.Size25dp
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.quartz.events.EventInterface
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun DisplayZapSplits(noteEvent: EventInterface, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val list = remember(noteEvent) { noteEvent.zapSplitSetup() }
if (list.isEmpty()) return
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
Modifier
.height(20.dp)
.width(25.dp)
) {
Icon(
imageVector = Icons.Outlined.Bolt,
contentDescription = stringResource(id = R.string.zaps),
modifier = Modifier
.size(20.dp)
.align(Alignment.CenterStart),
tint = BitcoinOrange
)
Icon(
imageVector = Icons.Outlined.ArrowForwardIos,
contentDescription = stringResource(id = R.string.zaps),
modifier = Modifier
.size(13.dp)
.align(Alignment.CenterEnd),
tint = BitcoinOrange
)
}
Spacer(modifier = StdHorzSpacer)
FlowRow {
list.forEach {
if (it.isLnAddress) {
ClickableText(
text = AnnotatedString(it.lnAddressOrPubKeyHex),
onClick = { },
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
} else {
UserPicture(
userHex = it.lnAddressOrPubKeyHex,
size = Size25dp,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
}
}
@@ -0,0 +1,58 @@
package com.vitorpamplona.amethyst.ui.layouts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.ui.note.RepostedIcon
import com.vitorpamplona.amethyst.ui.theme.Size18Modifier
import com.vitorpamplona.amethyst.ui.theme.Size35Modifier
import com.vitorpamplona.amethyst.ui.theme.Size55Modifier
import com.vitorpamplona.amethyst.ui.theme.placeholderText
@Composable
@Preview
private fun GenericRepostSectionPreview() {
GenericRepostLayout(
baseAuthorPicture = {
Text("ab")
},
repostAuthorPicture = {
Text("cd")
}
)
}
@Composable
fun GenericRepostLayout(
baseAuthorPicture: @Composable () -> Unit,
repostAuthorPicture: @Composable () -> Unit
) {
Box(modifier = Size55Modifier) {
Box(remember { Size35Modifier.align(Alignment.TopStart) }) {
baseAuthorPicture()
}
Box(
remember {
Size18Modifier
.align(Alignment.BottomStart)
.padding(1.dp)
}
) {
RepostedIcon(modifier = Size18Modifier, MaterialTheme.colorScheme.placeholderText)
}
Box(
remember { Size35Modifier.align(Alignment.BottomEnd) },
contentAlignment = Alignment.BottomEnd
) {
repostAuthorPicture()
}
}
}
@@ -0,0 +1,67 @@
package com.vitorpamplona.amethyst.ui.navigation
import com.vitorpamplona.amethyst.model.Channel
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.events.ChannelCreateEvent
import com.vitorpamplona.quartz.events.ChannelMessageEvent
import com.vitorpamplona.quartz.events.ChannelMetadataEvent
import com.vitorpamplona.quartz.events.ChatroomKey
import com.vitorpamplona.quartz.events.ChatroomKeyable
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.events.LiveActivitiesEvent
import kotlinx.collections.immutable.persistentSetOf
import java.net.URLEncoder
fun routeFor(note: Note, loggedIn: User): String? {
val noteEvent = note.event
if (noteEvent is ChannelMessageEvent || noteEvent is ChannelCreateEvent || noteEvent is ChannelMetadataEvent) {
note.channelHex()?.let {
return "Channel/$it"
}
} else if (noteEvent is LiveActivitiesEvent || noteEvent is LiveActivitiesChatMessageEvent) {
note.channelHex()?.let {
return "Channel/${URLEncoder.encode(it, "utf-8")}"
}
} else if (noteEvent is ChatroomKeyable) {
val room = noteEvent.chatroomKey(loggedIn.pubkeyHex)
loggedIn.createChatroom(room)
return "Room/${room.hashCode()}"
} else if (noteEvent is CommunityDefinitionEvent) {
return "Community/${URLEncoder.encode(note.idHex, "utf-8")}"
} else {
return "Note/${URLEncoder.encode(note.idHex, "utf-8")}"
}
return null
}
fun routeToMessage(user: HexKey, draftMessage: String?, accountViewModel: AccountViewModel): String {
val withKey = ChatroomKey(persistentSetOf(user))
accountViewModel.account.userProfile().createChatroom(withKey)
return if (draftMessage != null) {
"Room/${withKey.hashCode()}?message=$draftMessage"
} else {
"Room/${withKey.hashCode()}"
}
}
fun routeToMessage(user: User, draftMessage: String?, accountViewModel: AccountViewModel): String {
return routeToMessage(user.pubkeyHex, draftMessage, accountViewModel)
}
fun routeFor(note: Channel): String {
return "Channel/${note.idHex}"
}
fun routeFor(user: User): String {
return "User/${user.pubkeyHex}"
}
fun authorRouteFor(note: Note): String {
return "User/${note.author?.pubkeyHex}"
}
@@ -33,6 +33,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.screen.BadgeCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
@@ -22,6 +22,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.screen.MessageSetCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
@@ -49,6 +49,8 @@ import com.vitorpamplona.amethyst.ui.components.ImageUrlType
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.RobohashFallbackAsyncImage
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.navigation.authorRouteFor
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.screen.CombinedZap
import com.vitorpamplona.amethyst.ui.screen.MultiSetCard
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -34,12 +34,12 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.ui.note.LoadStatuses
import com.vitorpamplona.amethyst.ui.note.NIP05CheckingIcon
import com.vitorpamplona.amethyst.ui.note.NIP05FailedVerification
import com.vitorpamplona.amethyst.ui.note.NIP05VerifiedIcon
import com.vitorpamplona.amethyst.ui.note.routeFor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.NIP05IconSize
@@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -28,13 +27,7 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowForwardIos
import androidx.compose.material.icons.outlined.Bolt
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
@@ -44,7 +37,6 @@ import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@@ -53,7 +45,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.BottomEnd
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -71,7 +62,6 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.graphics.drawable.toBitmap
@@ -114,6 +104,17 @@ import com.vitorpamplona.amethyst.ui.components.figureOutMimeType
import com.vitorpamplona.amethyst.ui.components.imageExtensions
import com.vitorpamplona.amethyst.ui.components.measureSpaceWidth
import com.vitorpamplona.amethyst.ui.components.removeQueryParamsForExtensionComparison
import com.vitorpamplona.amethyst.ui.elements.AddButton
import com.vitorpamplona.amethyst.ui.elements.DisplayFollowingCommunityInPost
import com.vitorpamplona.amethyst.ui.elements.DisplayFollowingHashtagsInPost
import com.vitorpamplona.amethyst.ui.elements.DisplayPoW
import com.vitorpamplona.amethyst.ui.elements.DisplayReward
import com.vitorpamplona.amethyst.ui.elements.DisplayUncitedHashtags
import com.vitorpamplona.amethyst.ui.elements.DisplayZapSplits
import com.vitorpamplona.amethyst.ui.elements.RemoveButton
import com.vitorpamplona.amethyst.ui.elements.Reward
import com.vitorpamplona.amethyst.ui.layouts.GenericRepostLayout
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.screen.equalImmutableLists
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ChannelHeader
@@ -124,8 +125,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.LeaveCommunityButton
import com.vitorpamplona.amethyst.ui.screen.loggedIn.LiveFlag
import com.vitorpamplona.amethyst.ui.screen.loggedIn.NormalTimeAgo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ScheduledFlag
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
@@ -133,14 +132,11 @@ import com.vitorpamplona.amethyst.ui.theme.Font14SP
import com.vitorpamplona.amethyst.ui.theme.HalfDoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.HalfPadding
import com.vitorpamplona.amethyst.ui.theme.HalfStartPadding
import com.vitorpamplona.amethyst.ui.theme.HalfTopPadding
import com.vitorpamplona.amethyst.ui.theme.HeaderPictureModifier
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size15Modifier
import com.vitorpamplona.amethyst.ui.theme.Size16Modifier
import com.vitorpamplona.amethyst.ui.theme.Size18Modifier
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.Size24Modifier
import com.vitorpamplona.amethyst.ui.theme.Size25dp
import com.vitorpamplona.amethyst.ui.theme.Size30Modifier
@@ -159,7 +155,6 @@ import com.vitorpamplona.amethyst.ui.theme.UserNameRowHeight
import com.vitorpamplona.amethyst.ui.theme.WidthAuthorPictureModifier
import com.vitorpamplona.amethyst.ui.theme.channelNotePictureModifier
import com.vitorpamplona.amethyst.ui.theme.grayText
import com.vitorpamplona.amethyst.ui.theme.lessImportantLink
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
import com.vitorpamplona.amethyst.ui.theme.placeholderText
@@ -167,7 +162,6 @@ import com.vitorpamplona.amethyst.ui.theme.replyBackground
import com.vitorpamplona.amethyst.ui.theme.replyModifier
import com.vitorpamplona.amethyst.ui.theme.subtleBorder
import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.encoders.toNpub
import com.vitorpamplona.quartz.events.AppDefinitionEvent
import com.vitorpamplona.quartz.events.AudioHeaderEvent
@@ -178,8 +172,6 @@ import com.vitorpamplona.quartz.events.BaseTextNoteEvent
import com.vitorpamplona.quartz.events.ChannelCreateEvent
import com.vitorpamplona.quartz.events.ChannelMessageEvent
import com.vitorpamplona.quartz.events.ChannelMetadataEvent
import com.vitorpamplona.quartz.events.ChatroomKey
import com.vitorpamplona.quartz.events.ChatroomKeyable
import com.vitorpamplona.quartz.events.ClassifiedsEvent
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
@@ -187,7 +179,6 @@ import com.vitorpamplona.quartz.events.EmojiPackEvent
import com.vitorpamplona.quartz.events.EmojiPackSelectionEvent
import com.vitorpamplona.quartz.events.EmojiUrl
import com.vitorpamplona.quartz.events.EmptyTagList
import com.vitorpamplona.quartz.events.EventInterface
import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
import com.vitorpamplona.quartz.events.GenericRepostEvent
@@ -212,14 +203,11 @@ import com.vitorpamplona.quartz.events.UserMetadata
import com.vitorpamplona.quartz.events.toImmutableListOfLists
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import java.math.BigDecimal
import java.net.URL
import java.net.URLEncoder
import java.util.Locale
@OptIn(ExperimentalFoundationApi::class)
@@ -1070,60 +1058,6 @@ private fun NoteBody(
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun DisplayZapSplits(noteEvent: EventInterface, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
val list = remember(noteEvent) { noteEvent.zapSplitSetup() }
if (list.isEmpty()) return
Row(verticalAlignment = CenterVertically) {
Box(
Modifier
.height(20.dp)
.width(25.dp)
) {
Icon(
imageVector = Icons.Outlined.Bolt,
contentDescription = stringResource(id = R.string.zaps),
modifier = Modifier
.size(20.dp)
.align(Alignment.CenterStart),
tint = BitcoinOrange
)
Icon(
imageVector = Icons.Outlined.ArrowForwardIos,
contentDescription = stringResource(id = R.string.zaps),
modifier = Modifier
.size(13.dp)
.align(Alignment.CenterEnd),
tint = BitcoinOrange
)
}
Spacer(modifier = StdHorzSpacer)
FlowRow {
list.forEach {
if (it.isLnAddress) {
ClickableText(
text = AnnotatedString(it.lnAddressOrPubKeyHex),
onClick = { },
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.primary)
)
} else {
UserPicture(
userHex = it.lnAddressOrPubKeyHex,
size = Size25dp,
accountViewModel = accountViewModel,
nav = nav
)
}
}
}
}
}
@Composable
private fun RenderNoteRow(
baseNote: Note,
@@ -1265,56 +1199,6 @@ private fun RenderNoteRow(
}
}
fun routeFor(note: Note, loggedIn: User): String? {
val noteEvent = note.event
if (noteEvent is ChannelMessageEvent || noteEvent is ChannelCreateEvent || noteEvent is ChannelMetadataEvent) {
note.channelHex()?.let {
return "Channel/$it"
}
} else if (noteEvent is LiveActivitiesEvent || noteEvent is LiveActivitiesChatMessageEvent) {
note.channelHex()?.let {
return "Channel/${URLEncoder.encode(it, "utf-8")}"
}
} else if (noteEvent is ChatroomKeyable) {
val room = noteEvent.chatroomKey(loggedIn.pubkeyHex)
loggedIn.createChatroom(room)
return "Room/${room.hashCode()}"
} else if (noteEvent is CommunityDefinitionEvent) {
return "Community/${URLEncoder.encode(note.idHex, "utf-8")}"
} else {
return "Note/${URLEncoder.encode(note.idHex, "utf-8")}"
}
return null
}
fun routeToMessage(user: HexKey, draftMessage: String?, accountViewModel: AccountViewModel): String {
val withKey = ChatroomKey(persistentSetOf(user))
accountViewModel.account.userProfile().createChatroom(withKey)
return if (draftMessage != null) {
"Room/${withKey.hashCode()}?message=$draftMessage"
} else {
"Room/${withKey.hashCode()}"
}
}
fun routeToMessage(user: User, draftMessage: String?, accountViewModel: AccountViewModel): String {
return routeToMessage(user.pubkeyHex, draftMessage, accountViewModel)
}
fun routeFor(note: Channel): String {
return "Channel/${note.idHex}"
}
fun routeFor(user: User): String {
return "User/${user.pubkeyHex}"
}
fun authorRouteFor(note: Note): String {
return "User/${note.author?.pubkeyHex}"
}
@Composable
fun LoadDecryptedContent(
note: Note,
@@ -2289,45 +2173,6 @@ private fun EmojiListOptions(
}
}
@Composable
fun RemoveButton(onClick: () -> Unit) {
Button(
modifier = Modifier.padding(start = 3.dp),
onClick = onClick,
shape = ButtonBorder,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
),
contentPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)
) {
Text(text = stringResource(R.string.remove), color = Color.White)
}
}
@Composable
fun AddButton(
text: Int = R.string.add,
isActive: Boolean = true,
modifier: Modifier = Modifier.padding(start = 3.dp),
onClick: () -> Unit
) {
Button(
modifier = modifier,
onClick = {
if (isActive) {
onClick()
}
},
shape = ButtonBorder,
colors = ButtonDefaults.buttonColors(
containerColor = if (isActive) MaterialTheme.colorScheme.primary else Color.Gray
),
contentPadding = PaddingValues(vertical = 0.dp, horizontal = 16.dp)
) {
Text(text = stringResource(text), color = Color.White, textAlign = TextAlign.Center)
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun RenderPinListEvent(
@@ -2901,7 +2746,7 @@ private fun RepostNoteAuthorPicture(
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
GenericRepostSection(
GenericRepostLayout(
baseAuthorPicture = {
NoteAuthorPicture(
baseNote = baseNote,
@@ -2921,45 +2766,6 @@ private fun RepostNoteAuthorPicture(
)
}
@Composable
@Preview
private fun GenericRepostSectionPreview() {
GenericRepostSection(
baseAuthorPicture = {
Text("ab")
},
repostAuthorPicture = {
Text("cd")
}
)
}
@Composable
private fun GenericRepostSection(
baseAuthorPicture: @Composable () -> Unit,
repostAuthorPicture: @Composable () -> Unit
) {
Box(modifier = Size55Modifier) {
Box(remember { Size35Modifier.align(Alignment.TopStart) }) {
baseAuthorPicture()
}
Box(
remember {
Size18Modifier
.align(Alignment.BottomStart)
.padding(1.dp)
}
) {
RepostedIcon(modifier = Size18Modifier, MaterialTheme.colorScheme.placeholderText)
}
Box(remember { Size35Modifier.align(Alignment.BottomEnd) }, contentAlignment = BottomEnd) {
repostAuthorPicture()
}
}
}
@Composable
fun DisplayHighlight(
highlight: String,
@@ -3095,232 +2901,6 @@ private fun LoadAndDisplayUser(
}
}
@Composable
fun DisplayFollowingCommunityInPost(
baseNote: Note,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
Column(HalfStartPadding) {
Row(verticalAlignment = CenterVertically) {
DisplayCommunity(baseNote, nav)
}
}
}
@Composable
fun DisplayFollowingHashtagsInPost(
baseNote: Note,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
val noteEvent = remember { baseNote.event } ?: return
val userFollowState by accountViewModel.userFollows.observeAsState()
var firstTag by remember { mutableStateOf<String?>(null) }
LaunchedEffect(key1 = userFollowState) {
launch(Dispatchers.Default) {
val followingTags = userFollowState?.user?.cachedFollowingTagSet() ?: emptySet()
val newFirstTag = noteEvent.firstIsTaggedHashes(followingTags)
if (firstTag != newFirstTag) {
launch(Dispatchers.Main) {
firstTag = newFirstTag
}
}
}
}
firstTag?.let {
Column(verticalArrangement = Arrangement.Center) {
Row(verticalAlignment = CenterVertically) {
DisplayTagList(it, nav)
}
}
}
}
@Composable
private fun DisplayTagList(firstTag: String, nav: (String) -> Unit) {
val displayTag = remember(firstTag) { AnnotatedString(" #$firstTag") }
val route = remember(firstTag) { "Hashtag/$firstTag" }
ClickableText(
text = displayTag,
onClick = { nav(route) },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
),
maxLines = 1
)
}
@Composable
private fun DisplayCommunity(note: Note, nav: (String) -> Unit) {
val communityTag = remember(note) {
note.event?.getTagOfAddressableKind(CommunityDefinitionEvent.kind)
} ?: return
val displayTag = remember(note) { AnnotatedString(getCommunityShortName(communityTag)) }
val route = remember(note) { "Community/${communityTag.toTag()}" }
ClickableText(
text = displayTag,
onClick = { nav(route) },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
),
maxLines = 1
)
}
private fun getCommunityShortName(communityTag: ATag): String {
val name = if (communityTag.dTag.length > 10) {
communityTag.dTag.take(10) + "..."
} else {
communityTag.dTag.take(10)
}
return "/n/$name"
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun DisplayUncitedHashtags(
hashtags: ImmutableList<String>,
eventContent: String,
nav: (String) -> Unit
) {
val unusedHashtags = remember(eventContent) {
hashtags.filter { !eventContent.contains(it, true) }
}
if (unusedHashtags.isNotEmpty()) {
FlowRow(
modifier = HalfTopPadding
) {
unusedHashtags.forEach { hashtag ->
ClickableText(
text = remember { AnnotatedString("#$hashtag ") },
onClick = { nav("Hashtag/$hashtag") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.lessImportantLink
)
)
}
}
}
}
@Composable
fun DisplayPoW(
pow: Int
) {
val powStr = remember(pow) {
"PoW-$pow"
}
Text(
powStr,
color = MaterialTheme.colorScheme.lessImportantLink,
fontSize = Font14SP,
fontWeight = FontWeight.Bold,
maxLines = 1
)
}
@Stable
data class Reward(val amount: BigDecimal)
@Composable
fun DisplayReward(
baseReward: Reward,
baseNote: Note,
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
var popupExpanded by remember { mutableStateOf(false) }
Column() {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.clickable { popupExpanded = true }
) {
ClickableText(
text = AnnotatedString("#bounty"),
onClick = { nav("Hashtag/bounty") },
style = LocalTextStyle.current.copy(
color = MaterialTheme.colorScheme.primary.copy(
alpha = 0.52f
)
)
)
RenderPledgeAmount(baseNote, baseReward, accountViewModel)
}
if (popupExpanded) {
AddBountyAmountDialog(baseNote, accountViewModel) {
popupExpanded = false
}
}
}
}
@Composable
private fun RenderPledgeAmount(
baseNote: Note,
baseReward: Reward,
accountViewModel: AccountViewModel
) {
val repliesState by baseNote.live().replies.observeAsState()
var reward by remember {
mutableStateOf<String>(
showAmount(baseReward.amount)
)
}
var hasPledge by remember {
mutableStateOf<Boolean>(
false
)
}
LaunchedEffect(key1 = repliesState) {
launch(Dispatchers.IO) {
repliesState?.note?.pledgedAmountByOthers()?.let {
val newRewardAmount = showAmount(baseReward.amount.add(it))
if (newRewardAmount != reward) {
reward = newRewardAmount
}
}
val newHasPledge = repliesState?.note?.hasPledgeBy(accountViewModel.userProfile()) == true
if (hasPledge != newHasPledge) {
launch(Dispatchers.Main) {
hasPledge = newHasPledge
}
}
}
}
if (hasPledge) {
ZappedIcon(modifier = Size20Modifier)
} else {
ZapIcon(modifier = Size20Modifier, MaterialTheme.colorScheme.placeholderText)
}
Text(
text = reward,
color = MaterialTheme.colorScheme.placeholderText,
maxLines = 1
)
}
@Composable
fun BadgeDisplay(baseNote: Note) {
val background = MaterialTheme.colorScheme.background
@@ -54,6 +54,7 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.ZapPaymentHandler
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.StringToastMsg
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
@@ -70,7 +71,6 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*
import kotlin.math.roundToInt
@Composable
@@ -13,7 +13,6 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.animation.with
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -83,6 +82,7 @@ import com.vitorpamplona.amethyst.ui.actions.NewPostView
import com.vitorpamplona.amethyst.ui.components.ImageUrlType
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.TextType
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.DarkerGreen
@@ -714,17 +714,12 @@ fun LikeReaction(
indication = rememberRipple(bounded = false, radius = Size24dp),
onClick = {
likeClick(
baseNote,
accountViewModel,
onMultipleChoices = {
wantsToReact = true
},
onWantsToSignReaction = {
if (accountViewModel.account.reactionChoices.size == 1) {
accountViewModel.reactToOrDelete(baseNote)
} else if (accountViewModel.account.reactionChoices.size > 1) {
wantsToReact = true
}
accountViewModel.reactToOrDelete(baseNote)
}
)
},
@@ -835,7 +830,6 @@ fun ObserveLikeText(baseNote: Note, inner: @Composable (Int) -> Unit) {
}
private fun likeClick(
baseNote: Note,
accountViewModel: AccountViewModel,
onMultipleChoices: () -> Unit,
onWantsToSignReaction: () -> Unit
@@ -851,7 +845,7 @@ private fun likeClick(
R.string.login_with_a_private_key_to_like_posts
)
} else if (accountViewModel.account.reactionChoices.size == 1) {
accountViewModel.reactToOrDelete(baseNote)
onWantsToSignReaction()
} else if (accountViewModel.account.reactionChoices.size > 1) {
onMultipleChoices()
}
@@ -63,6 +63,7 @@ import com.vitorpamplona.amethyst.ui.actions.SaveButton
import com.vitorpamplona.amethyst.ui.components.ImageUrlType
import com.vitorpamplona.amethyst.ui.components.InLineIconRenderer
import com.vitorpamplona.amethyst.ui.components.TextType
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.placeholderText
@@ -3,7 +3,6 @@ package com.vitorpamplona.amethyst.ui.screen
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
@@ -65,20 +64,22 @@ import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.InlineCarrousel
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
import com.vitorpamplona.amethyst.ui.elements.DisplayFollowingCommunityInPost
import com.vitorpamplona.amethyst.ui.elements.DisplayFollowingHashtagsInPost
import com.vitorpamplona.amethyst.ui.elements.DisplayPoW
import com.vitorpamplona.amethyst.ui.elements.DisplayReward
import com.vitorpamplona.amethyst.ui.elements.DisplayZapSplits
import com.vitorpamplona.amethyst.ui.elements.Reward
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
import com.vitorpamplona.amethyst.ui.note.AudioHeader
import com.vitorpamplona.amethyst.ui.note.AudioTrackHeader
import com.vitorpamplona.amethyst.ui.note.BadgeDisplay
import com.vitorpamplona.amethyst.ui.note.BlankNote
import com.vitorpamplona.amethyst.ui.note.CreateImageHeader
import com.vitorpamplona.amethyst.ui.note.DisplayFollowingCommunityInPost
import com.vitorpamplona.amethyst.ui.note.DisplayFollowingHashtagsInPost
import com.vitorpamplona.amethyst.ui.note.DisplayHighlight
import com.vitorpamplona.amethyst.ui.note.DisplayLocation
import com.vitorpamplona.amethyst.ui.note.DisplayPeopleList
import com.vitorpamplona.amethyst.ui.note.DisplayPoW
import com.vitorpamplona.amethyst.ui.note.DisplayRelaySet
import com.vitorpamplona.amethyst.ui.note.DisplayReward
import com.vitorpamplona.amethyst.ui.note.DisplayZapSplits
import com.vitorpamplona.amethyst.ui.note.FileHeaderDisplay
import com.vitorpamplona.amethyst.ui.note.FileStorageHeaderDisplay
import com.vitorpamplona.amethyst.ui.note.HiddenNote
@@ -95,8 +96,6 @@ import com.vitorpamplona.amethyst.ui.note.RenderPoll
import com.vitorpamplona.amethyst.ui.note.RenderPostApproval
import com.vitorpamplona.amethyst.ui.note.RenderRepost
import com.vitorpamplona.amethyst.ui.note.RenderTextEvent
import com.vitorpamplona.amethyst.ui.note.Reward
import com.vitorpamplona.amethyst.ui.note.routeToMessage
import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.amethyst.ui.note.timeAgo
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -98,9 +98,10 @@ import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.components.ZoomableUrlVideo
import com.vitorpamplona.amethyst.ui.elements.DisplayUncitedHashtags
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.note.ChatroomMessageCompose
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
import com.vitorpamplona.amethyst.ui.note.DisplayUncitedHashtags
import com.vitorpamplona.amethyst.ui.note.LikeReaction
import com.vitorpamplona.amethyst.ui.note.LoadChannel
import com.vitorpamplona.amethyst.ui.note.MoreOptionsButton
@@ -109,7 +110,6 @@ import com.vitorpamplona.amethyst.ui.note.NoteUsernameDisplay
import com.vitorpamplona.amethyst.ui.note.UserPicture
import com.vitorpamplona.amethyst.ui.note.UsernameDisplay
import com.vitorpamplona.amethyst.ui.note.ZapReaction
import com.vitorpamplona.amethyst.ui.note.routeFor
import com.vitorpamplona.amethyst.ui.note.timeAgo
import com.vitorpamplona.amethyst.ui.note.timeAgoShort
import com.vitorpamplona.amethyst.ui.screen.NostrChannelFeedViewModel
@@ -47,7 +47,7 @@ import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.ui.note.AddButton
import com.vitorpamplona.amethyst.ui.elements.AddButton
import com.vitorpamplona.amethyst.ui.screen.NostrHiddenAccountsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrHiddenWordsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrSpammerAccountsFeedViewModel
@@ -109,12 +109,12 @@ import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.components.ZoomableImageDialog
import com.vitorpamplona.amethyst.ui.components.figureOutMimeType
import com.vitorpamplona.amethyst.ui.dal.UserProfileReportsFeedFilter
import com.vitorpamplona.amethyst.ui.navigation.routeToMessage
import com.vitorpamplona.amethyst.ui.note.ClickableUserPicture
import com.vitorpamplona.amethyst.ui.note.ErrorMessageDialog
import com.vitorpamplona.amethyst.ui.note.LightningAddressIcon
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
import com.vitorpamplona.amethyst.ui.note.payViaIntent
import com.vitorpamplona.amethyst.ui.note.routeToMessage
import com.vitorpamplona.amethyst.ui.qrcode.ShowQRDialog
import com.vitorpamplona.amethyst.ui.screen.FeedState
import com.vitorpamplona.amethyst.ui.screen.LnZapFeedView
@@ -46,6 +46,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.NostrVideoDataSource
import com.vitorpamplona.amethyst.ui.actions.NewPostView
import com.vitorpamplona.amethyst.ui.components.ObserveDisplayNip05Status
import com.vitorpamplona.amethyst.ui.navigation.routeFor
import com.vitorpamplona.amethyst.ui.note.BoostReaction
import com.vitorpamplona.amethyst.ui.note.FileHeaderDisplay
import com.vitorpamplona.amethyst.ui.note.FileStorageHeaderDisplay
@@ -59,7 +60,6 @@ import com.vitorpamplona.amethyst.ui.note.ReplyReaction
import com.vitorpamplona.amethyst.ui.note.ViewCountReaction
import com.vitorpamplona.amethyst.ui.note.WatchForReports
import com.vitorpamplona.amethyst.ui.note.ZapReaction
import com.vitorpamplona.amethyst.ui.note.routeFor
import com.vitorpamplona.amethyst.ui.screen.FeedEmpty
import com.vitorpamplona.amethyst.ui.screen.FeedError
import com.vitorpamplona.amethyst.ui.screen.FeedState
@@ -4,12 +4,14 @@ import android.app.Activity
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
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.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
@@ -27,6 +29,7 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.halilibo.richtext.ui.RichTextStyle
import com.halilibo.richtext.ui.resolveDefaults
import com.patrykandpatrick.vico.compose.style.ChartStyle
@@ -367,3 +370,27 @@ fun AmethystTheme(sharedPrefsViewModel: SharedPreferencesViewModel, content: @Co
}
}
}
@Composable
fun ThemeComparison(
onDark: @Composable () -> Unit,
onLight: @Composable () -> Unit
) {
Column() {
val darkTheme: SharedPreferencesViewModel = viewModel()
darkTheme.updateTheme(ThemeType.DARK)
AmethystTheme(darkTheme) {
Surface(color = MaterialTheme.colorScheme.background) {
onDark()
}
}
val lightTheme: SharedPreferencesViewModel = viewModel()
lightTheme.updateTheme(ThemeType.LIGHT)
AmethystTheme(lightTheme) {
Surface(color = MaterialTheme.colorScheme.background) {
onLight()
}
}
}
}