Visual support for Kind1 forks.
This commit is contained in:
@@ -1325,6 +1325,7 @@ class Account(
|
|||||||
replyingTo: String?,
|
replyingTo: String?,
|
||||||
root: String?,
|
root: String?,
|
||||||
directMentions: Set<HexKey>,
|
directMentions: Set<HexKey>,
|
||||||
|
forkedFrom: Event?,
|
||||||
relayList: List<Relay>? = null,
|
relayList: List<Relay>? = null,
|
||||||
geohash: String? = null,
|
geohash: String? = null,
|
||||||
nip94attachments: List<FileHeaderEvent>? = null,
|
nip94attachments: List<FileHeaderEvent>? = null,
|
||||||
@@ -1349,6 +1350,7 @@ class Account(
|
|||||||
directMentions = directMentions,
|
directMentions = directMentions,
|
||||||
geohash = geohash,
|
geohash = geohash,
|
||||||
nip94attachments = nip94attachments,
|
nip94attachments = nip94attachments,
|
||||||
|
forkedFrom = forkedFrom,
|
||||||
signer = signer,
|
signer = signer,
|
||||||
) {
|
) {
|
||||||
Client.send(it, relayList = relayList)
|
Client.send(it, relayList = relayList)
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ fun NewPostView(
|
|||||||
onClose: () -> Unit,
|
onClose: () -> Unit,
|
||||||
baseReplyTo: Note? = null,
|
baseReplyTo: Note? = null,
|
||||||
quote: Note? = null,
|
quote: Note? = null,
|
||||||
|
fork: Note? = null,
|
||||||
enableMessageInterface: Boolean = false,
|
enableMessageInterface: Boolean = false,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
@@ -201,7 +202,7 @@ fun NewPostView(
|
|||||||
var relayList = remember { accountViewModel.account.activeWriteRelays().toImmutableList() }
|
var relayList = remember { accountViewModel.account.activeWriteRelays().toImmutableList() }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
postViewModel.load(accountViewModel, baseReplyTo, quote)
|
postViewModel.load(accountViewModel, baseReplyTo, quote, fork)
|
||||||
|
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
postViewModel.imageUploadingError.collect { error ->
|
postViewModel.imageUploadingError.collect { error ->
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import com.vitorpamplona.quartz.events.BaseTextNoteEvent
|
|||||||
import com.vitorpamplona.quartz.events.ChatMessageEvent
|
import com.vitorpamplona.quartz.events.ChatMessageEvent
|
||||||
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
import com.vitorpamplona.quartz.events.ClassifiedsEvent
|
||||||
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
|
||||||
|
import com.vitorpamplona.quartz.events.Event
|
||||||
import com.vitorpamplona.quartz.events.FileHeaderEvent
|
import com.vitorpamplona.quartz.events.FileHeaderEvent
|
||||||
import com.vitorpamplona.quartz.events.FileStorageEvent
|
import com.vitorpamplona.quartz.events.FileStorageEvent
|
||||||
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
|
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
|
||||||
@@ -85,10 +86,10 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
var requiresNIP24: Boolean = false
|
var requiresNIP24: Boolean = false
|
||||||
|
|
||||||
var originalNote: Note? = null
|
var originalNote: Note? = null
|
||||||
|
var forkedFromNote: Note? = null
|
||||||
|
|
||||||
var pTags by mutableStateOf<List<User>?>(null)
|
var pTags by mutableStateOf<List<User>?>(null)
|
||||||
var eTags by mutableStateOf<List<Note>?>(null)
|
var eTags by mutableStateOf<List<Note>?>(null)
|
||||||
var imetaTags = mutableStateListOf<Array<String>>()
|
|
||||||
|
|
||||||
var nip94attachments by mutableStateOf<List<FileHeaderEvent>>(emptyList())
|
var nip94attachments by mutableStateOf<List<FileHeaderEvent>>(emptyList())
|
||||||
var nip95attachments by
|
var nip95attachments by
|
||||||
@@ -166,6 +167,7 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
replyingTo: Note?,
|
replyingTo: Note?,
|
||||||
quote: Note?,
|
quote: Note?,
|
||||||
|
fork: Note?,
|
||||||
) {
|
) {
|
||||||
this.accountViewModel = accountViewModel
|
this.accountViewModel = accountViewModel
|
||||||
this.account = accountViewModel.account
|
this.account = accountViewModel.account
|
||||||
@@ -214,6 +216,37 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
zapRaiserAmount = null
|
zapRaiserAmount = null
|
||||||
forwardZapTo = Split()
|
forwardZapTo = Split()
|
||||||
forwardZapToEditting = TextFieldValue("")
|
forwardZapToEditting = TextFieldValue("")
|
||||||
|
|
||||||
|
fork?.let {
|
||||||
|
message = TextFieldValue(it.event?.content() ?: "")
|
||||||
|
urlPreview = findUrlInMessage()
|
||||||
|
|
||||||
|
it.event?.isSensitive()?.let {
|
||||||
|
if (it) wantsToMarkAsSensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
it.event?.zapraiserAmount()?.let {
|
||||||
|
zapRaiserAmount = it
|
||||||
|
}
|
||||||
|
|
||||||
|
it.event?.zapSplitSetup()?.let {
|
||||||
|
val totalWeight = it.sumOf { if (it.isLnAddress) 0.0 else it.weight }
|
||||||
|
|
||||||
|
it.forEach {
|
||||||
|
if (!it.isLnAddress) {
|
||||||
|
forwardZapTo.addItem(LocalCache.getOrCreateUser(it.lnAddressOrPubKeyHex), (it.weight / totalWeight).toFloat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it.author?.let {
|
||||||
|
if (this.pTags?.contains(it) != true) {
|
||||||
|
this.pTags = listOf(it) + (this.pTags ?: emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
forkedFromNote = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendPost(relayList: List<Relay>? = null) {
|
fun sendPost(relayList: List<Relay>? = null) {
|
||||||
@@ -404,9 +437,16 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
|
|
||||||
val replyId = originalNote?.idHex
|
val replyId = originalNote?.idHex
|
||||||
|
|
||||||
|
val replyToSet =
|
||||||
|
if (forkedFromNote != null) {
|
||||||
|
(listOfNotNull(forkedFromNote) + (tagger.eTags ?: emptyList())).ifEmpty { null }
|
||||||
|
} else {
|
||||||
|
tagger.eTags
|
||||||
|
}
|
||||||
|
|
||||||
account?.sendPost(
|
account?.sendPost(
|
||||||
message = tagger.message,
|
message = tagger.message,
|
||||||
replyTo = tagger.eTags,
|
replyTo = replyToSet,
|
||||||
mentions = tagger.pTags,
|
mentions = tagger.pTags,
|
||||||
tags = null,
|
tags = null,
|
||||||
zapReceiver = zapReceiver,
|
zapReceiver = zapReceiver,
|
||||||
@@ -415,6 +455,7 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
replyingTo = replyId,
|
replyingTo = replyId,
|
||||||
root = rootId,
|
root = rootId,
|
||||||
directMentions = tagger.directMentions,
|
directMentions = tagger.directMentions,
|
||||||
|
forkedFrom = forkedFromNote?.event as? Event,
|
||||||
relayList = relayList,
|
relayList = relayList,
|
||||||
geohash = geoHash,
|
geohash = geoHash,
|
||||||
nip94attachments = usedAttachments,
|
nip94attachments = usedAttachments,
|
||||||
@@ -504,7 +545,6 @@ open class NewPostViewModel() : ViewModel() {
|
|||||||
urlPreview = null
|
urlPreview = null
|
||||||
isUploadingImage = false
|
isUploadingImage = false
|
||||||
pTags = null
|
pTags = null
|
||||||
imetaTags.clear()
|
|
||||||
|
|
||||||
wantsDirectMessage = false
|
wantsDirectMessage = false
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ fun ChannelFabColumn(
|
|||||||
|
|
||||||
if (wantsToSendNewMessage) {
|
if (wantsToSendNewMessage) {
|
||||||
NewPostView(
|
NewPostView(
|
||||||
{ wantsToSendNewMessage = false },
|
onClose = { wantsToSendNewMessage = false },
|
||||||
enableMessageInterface = true,
|
enableMessageInterface = true,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
nav = nav,
|
nav = nav,
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ fun CreateClickableText(
|
|||||||
maxLines: Int = Int.MAX_VALUE,
|
maxLines: Int = Int.MAX_VALUE,
|
||||||
overrideColor: Color? = null,
|
overrideColor: Color? = null,
|
||||||
fontWeight: FontWeight? = null,
|
fontWeight: FontWeight? = null,
|
||||||
|
fontSize: TextUnit = TextUnit.Unspecified,
|
||||||
route: String,
|
route: String,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -304,12 +305,14 @@ fun CreateClickableText(
|
|||||||
remember(clickablePart, suffix) {
|
remember(clickablePart, suffix) {
|
||||||
val clickablePartStyle =
|
val clickablePartStyle =
|
||||||
SpanStyle(
|
SpanStyle(
|
||||||
|
fontSize = fontSize,
|
||||||
color = overrideColor ?: primaryColor,
|
color = overrideColor ?: primaryColor,
|
||||||
fontWeight = fontWeight,
|
fontWeight = fontWeight,
|
||||||
)
|
)
|
||||||
|
|
||||||
val nonClickablePartStyle =
|
val nonClickablePartStyle =
|
||||||
SpanStyle(
|
SpanStyle(
|
||||||
|
fontSize = fontSize,
|
||||||
color = overrideColor ?: onBackgroundColor,
|
color = overrideColor ?: onBackgroundColor,
|
||||||
fontWeight = fontWeight,
|
fontWeight = fontWeight,
|
||||||
)
|
)
|
||||||
@@ -562,6 +565,7 @@ fun CreateClickableTextWithEmoji(
|
|||||||
maxLines: Int = Int.MAX_VALUE,
|
maxLines: Int = Int.MAX_VALUE,
|
||||||
overrideColor: Color? = null,
|
overrideColor: Color? = null,
|
||||||
fontWeight: FontWeight = FontWeight.Normal,
|
fontWeight: FontWeight = FontWeight.Normal,
|
||||||
|
fontSize: TextUnit = TextUnit.Unspecified,
|
||||||
route: String,
|
route: String,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
tags: ImmutableListOfLists<String>?,
|
tags: ImmutableListOfLists<String>?,
|
||||||
@@ -570,11 +574,12 @@ fun CreateClickableTextWithEmoji(
|
|||||||
text = clickablePart,
|
text = clickablePart,
|
||||||
tags = tags,
|
tags = tags,
|
||||||
onRegularText = {
|
onRegularText = {
|
||||||
CreateClickableText(it, null, maxLines, overrideColor, fontWeight, route, nav)
|
CreateClickableText(it, null, maxLines, overrideColor, fontWeight, fontSize, route, nav)
|
||||||
},
|
},
|
||||||
onEmojiText = {
|
onEmojiText = {
|
||||||
val clickablePartStyle =
|
val clickablePartStyle =
|
||||||
SpanStyle(
|
SpanStyle(
|
||||||
|
fontSize = fontSize,
|
||||||
color = overrideColor ?: MaterialTheme.colorScheme.primary,
|
color = overrideColor ?: MaterialTheme.colorScheme.primary,
|
||||||
fontWeight = fontWeight,
|
fontWeight = fontWeight,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -32,6 +32,15 @@ class SplitItem<T>(val key: T) {
|
|||||||
class Split<T>() {
|
class Split<T>() {
|
||||||
var items: List<SplitItem<T>> by mutableStateOf(emptyList())
|
var items: List<SplitItem<T>> by mutableStateOf(emptyList())
|
||||||
|
|
||||||
|
fun addItem(
|
||||||
|
key: T,
|
||||||
|
percentage: Float,
|
||||||
|
) {
|
||||||
|
val newItem = SplitItem(key)
|
||||||
|
newItem.percentage = percentage
|
||||||
|
this.items = items.plus(newItem)
|
||||||
|
}
|
||||||
|
|
||||||
fun addItem(key: T): Int {
|
fun addItem(key: T): Int {
|
||||||
val wasEqualSplit = isEqualSplit()
|
val wasEqualSplit = isEqualSplit()
|
||||||
val newItem = SplitItem(key)
|
val newItem = SplitItem(key)
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ class AddBountyAmountViewModel : ViewModel() {
|
|||||||
replyingTo = null,
|
replyingTo = null,
|
||||||
root = null,
|
root = null,
|
||||||
directMentions = setOf(),
|
directMentions = setOf(),
|
||||||
|
forkedFrom = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
nextAmount = TextFieldValue("")
|
nextAmount = TextFieldValue("")
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ import androidx.compose.ui.platform.LocalUriHandler
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
@@ -178,6 +179,7 @@ import com.vitorpamplona.amethyst.ui.theme.grayText
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
import com.vitorpamplona.amethyst.ui.theme.imageModifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
|
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
|
||||||
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
|
import com.vitorpamplona.amethyst.ui.theme.newItemBackgroundColor
|
||||||
|
import com.vitorpamplona.amethyst.ui.theme.nip05
|
||||||
import com.vitorpamplona.amethyst.ui.theme.normalNoteModifier
|
import com.vitorpamplona.amethyst.ui.theme.normalNoteModifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.normalWithTopMarginNoteModifier
|
import com.vitorpamplona.amethyst.ui.theme.normalWithTopMarginNoteModifier
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
@@ -2512,28 +2514,32 @@ fun SecondUserInfoRow(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val noteEvent = remember { note.event } ?: return
|
val noteEvent = note.event ?: return
|
||||||
val noteAuthor = remember { note.author } ?: return
|
val noteAuthor = note.author ?: return
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = CenterVertically,
|
verticalAlignment = CenterVertically,
|
||||||
modifier = UserNameMaxRowHeight,
|
modifier = UserNameMaxRowHeight,
|
||||||
) {
|
) {
|
||||||
ObserveDisplayNip05Status(noteAuthor, remember { Modifier.weight(1f) }, accountViewModel, nav)
|
if (noteEvent is BaseTextNoteEvent && noteEvent.isAFork()) {
|
||||||
|
ShowForkInformation(noteEvent, remember(noteEvent) { Modifier.weight(1f) }, accountViewModel, nav)
|
||||||
|
} else {
|
||||||
|
ObserveDisplayNip05Status(noteAuthor, remember(noteEvent) { Modifier.weight(1f) }, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
|
||||||
val geo = remember { noteEvent.getGeoHash() }
|
val geo = remember(noteEvent) { noteEvent.getGeoHash() }
|
||||||
if (geo != null) {
|
if (geo != null) {
|
||||||
Spacer(StdHorzSpacer)
|
Spacer(StdHorzSpacer)
|
||||||
DisplayLocation(geo, nav)
|
DisplayLocation(geo, nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
val baseReward = remember { noteEvent.getReward()?.let { Reward(it) } }
|
val baseReward = remember(noteEvent) { noteEvent.getReward()?.let { Reward(it) } }
|
||||||
if (baseReward != null) {
|
if (baseReward != null) {
|
||||||
Spacer(StdHorzSpacer)
|
Spacer(StdHorzSpacer)
|
||||||
DisplayReward(baseReward, note, accountViewModel, nav)
|
DisplayReward(baseReward, note, accountViewModel, nav)
|
||||||
}
|
}
|
||||||
|
|
||||||
val pow = remember { noteEvent.getPoWRank() }
|
val pow = remember(noteEvent) { noteEvent.getPoWRank() }
|
||||||
if (pow > 20) {
|
if (pow > 20) {
|
||||||
Spacer(StdHorzSpacer)
|
Spacer(StdHorzSpacer)
|
||||||
DisplayPoW(pow)
|
DisplayPoW(pow)
|
||||||
@@ -2541,6 +2547,76 @@ fun SecondUserInfoRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ShowForkInformation(
|
||||||
|
noteEvent: BaseTextNoteEvent,
|
||||||
|
modifier: Modifier,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
val forkedAddress = remember(noteEvent) { noteEvent.forkFromAddress() }
|
||||||
|
val forkedEvent = remember(noteEvent) { noteEvent.forkFromVersion() }
|
||||||
|
if (forkedAddress != null) {
|
||||||
|
LoadAddressableNote(aTag = forkedAddress, accountViewModel = accountViewModel) { addressableNote ->
|
||||||
|
if (addressableNote != null) {
|
||||||
|
ForkInformationRowLightColor(addressableNote, modifier, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (forkedEvent != null) {
|
||||||
|
LoadNote(forkedEvent, accountViewModel = accountViewModel) { event ->
|
||||||
|
if (event != null) {
|
||||||
|
ForkInformationRowLightColor(event, modifier, accountViewModel, nav)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ForkInformationRowLightColor(
|
||||||
|
originalVersion: Note,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
accountViewModel: AccountViewModel,
|
||||||
|
nav: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
val noteState by originalVersion.live().metadata.observeAsState()
|
||||||
|
val note = noteState?.note ?: return
|
||||||
|
val author = note.author ?: return
|
||||||
|
val route = remember(note) { routeFor(note, accountViewModel.userProfile()) }
|
||||||
|
|
||||||
|
if (route != null) {
|
||||||
|
Row(modifier) {
|
||||||
|
ClickableText(
|
||||||
|
text =
|
||||||
|
buildAnnotatedString {
|
||||||
|
append(stringResource(id = R.string.forked_from))
|
||||||
|
append(" ")
|
||||||
|
},
|
||||||
|
onClick = { nav(route) },
|
||||||
|
style = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.nip05, fontSize = Font14SP),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Visible,
|
||||||
|
)
|
||||||
|
|
||||||
|
val userState by author.live().metadata.observeAsState()
|
||||||
|
val userDisplayName = remember(userState) { userState?.user?.toBestDisplayName() }
|
||||||
|
val userTags =
|
||||||
|
remember(userState) { userState?.user?.info?.latestMetadata?.tags?.toImmutableListOfLists() }
|
||||||
|
|
||||||
|
if (userDisplayName != null) {
|
||||||
|
CreateClickableTextWithEmoji(
|
||||||
|
clickablePart = userDisplayName,
|
||||||
|
maxLines = 1,
|
||||||
|
route = route,
|
||||||
|
overrideColor = MaterialTheme.colorScheme.nip05,
|
||||||
|
fontSize = Font14SP,
|
||||||
|
nav = nav,
|
||||||
|
tags = userTags,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoadStatuses(
|
fun LoadStatuses(
|
||||||
user: User,
|
user: User,
|
||||||
@@ -3928,7 +4004,6 @@ private fun WikiNoteHeader(
|
|||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
val title = remember(noteEvent) { noteEvent.title() }
|
val title = remember(noteEvent) { noteEvent.title() }
|
||||||
val forkedAddress = remember(noteEvent) { noteEvent.forkFromAddress() }
|
|
||||||
val summary =
|
val summary =
|
||||||
remember(noteEvent) {
|
remember(noteEvent) {
|
||||||
noteEvent.summary()?.ifBlank { null } ?: noteEvent.content.take(200).ifBlank { null }
|
noteEvent.summary()?.ifBlank { null } ?: noteEvent.content.take(200).ifBlank { null }
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
|
|||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||||
import com.vitorpamplona.amethyst.ui.theme.placeholderTextColorFilter
|
import com.vitorpamplona.amethyst.ui.theme.placeholderTextColorFilter
|
||||||
import com.vitorpamplona.quartz.encoders.Nip30CustomEmoji
|
import com.vitorpamplona.quartz.encoders.Nip30CustomEmoji
|
||||||
|
import com.vitorpamplona.quartz.events.BaseTextNoteEvent
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.ImmutableSet
|
import kotlinx.collections.immutable.ImmutableSet
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
@@ -499,6 +500,7 @@ private fun BoostWithDialog(
|
|||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
var wantsToQuote by remember { mutableStateOf<Note?>(null) }
|
var wantsToQuote by remember { mutableStateOf<Note?>(null) }
|
||||||
|
var wantsToFork by remember { mutableStateOf<Note?>(null) }
|
||||||
|
|
||||||
if (wantsToQuote != null) {
|
if (wantsToQuote != null) {
|
||||||
NewPostView(
|
NewPostView(
|
||||||
@@ -510,7 +512,34 @@ private fun BoostWithDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
BoostReaction(baseNote, grayTint, accountViewModel) { wantsToQuote = baseNote }
|
if (wantsToFork != null) {
|
||||||
|
val replyTo =
|
||||||
|
remember(wantsToFork) {
|
||||||
|
val forkEvent = wantsToFork?.event
|
||||||
|
if (forkEvent is BaseTextNoteEvent) {
|
||||||
|
val hex = forkEvent.replyingTo()
|
||||||
|
wantsToFork?.replyTo?.filter { it.event?.id() == hex }?.firstOrNull()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NewPostView(
|
||||||
|
onClose = { wantsToFork = null },
|
||||||
|
baseReplyTo = replyTo,
|
||||||
|
fork = wantsToFork,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
nav = nav,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BoostReaction(
|
||||||
|
baseNote,
|
||||||
|
grayTint,
|
||||||
|
accountViewModel,
|
||||||
|
onQuotePress = { wantsToQuote = baseNote },
|
||||||
|
onForkPress = { wantsToFork = baseNote },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -650,6 +679,7 @@ fun BoostReaction(
|
|||||||
iconSizeModifier: Modifier = Size20Modifier,
|
iconSizeModifier: Modifier = Size20Modifier,
|
||||||
iconSize: Dp = Size20dp,
|
iconSize: Dp = Size20dp,
|
||||||
onQuotePress: () -> Unit,
|
onQuotePress: () -> Unit,
|
||||||
|
onForkPress: () -> Unit,
|
||||||
) {
|
) {
|
||||||
var wantsToBoost by remember { mutableStateOf(false) }
|
var wantsToBoost by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -671,7 +701,13 @@ fun BoostReaction(
|
|||||||
wantsToBoost = false
|
wantsToBoost = false
|
||||||
onQuotePress()
|
onQuotePress()
|
||||||
},
|
},
|
||||||
onRepost = { accountViewModel.boost(baseNote) },
|
onRepost = {
|
||||||
|
accountViewModel.boost(baseNote)
|
||||||
|
},
|
||||||
|
onFork = {
|
||||||
|
wantsToBoost = false
|
||||||
|
onForkPress()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1149,6 +1185,7 @@ private fun BoostTypeChoicePopup(
|
|||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onQuote: () -> Unit,
|
onQuote: () -> Unit,
|
||||||
onRepost: () -> Unit,
|
onRepost: () -> Unit,
|
||||||
|
onFork: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val iconSizePx = with(LocalDensity.current) { -iconSize.toPx().toInt() }
|
val iconSizePx = with(LocalDensity.current) { -iconSize.toPx().toInt() }
|
||||||
|
|
||||||
@@ -1189,6 +1226,18 @@ private fun BoostTypeChoicePopup(
|
|||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.quote), color = Color.White, textAlign = TextAlign.Center)
|
Text(stringResource(R.string.quote), color = Color.White, textAlign = TextAlign.Center)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
modifier = Modifier.padding(horizontal = 3.dp),
|
||||||
|
onClick = onFork,
|
||||||
|
shape = ButtonBorder,
|
||||||
|
colors =
|
||||||
|
ButtonDefaults.buttonColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.fork), color = Color.White, textAlign = TextAlign.Center)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ import androidx.compose.ui.unit.sp
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.AddressableNote
|
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.ui.components.InlineCarrousel
|
import com.vitorpamplona.amethyst.ui.components.InlineCarrousel
|
||||||
@@ -913,7 +912,7 @@ private fun RenderWikiHeaderForThread(
|
|||||||
forkedAddress?.let {
|
forkedAddress?.let {
|
||||||
LoadAddressableNote(aTag = it, accountViewModel = accountViewModel) { originalVersion ->
|
LoadAddressableNote(aTag = it, accountViewModel = accountViewModel) { originalVersion ->
|
||||||
if (originalVersion != null) {
|
if (originalVersion != null) {
|
||||||
ShowForkInformation(originalVersion, Modifier.fillMaxWidth(), accountViewModel, nav)
|
ForkInformationRow(originalVersion, Modifier.fillMaxWidth(), accountViewModel, nav)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -934,8 +933,8 @@ private fun RenderWikiHeaderForThread(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ShowForkInformation(
|
fun ForkInformationRow(
|
||||||
originalVersion: AddressableNote,
|
originalVersion: Note,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: (String) -> Unit,
|
nav: (String) -> Unit,
|
||||||
|
|||||||
@@ -476,9 +476,12 @@ fun ReactionsColumn(
|
|||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
iconSizeModifier = Size40Modifier,
|
iconSizeModifier = Size40Modifier,
|
||||||
iconSize = Size40dp,
|
iconSize = Size40dp,
|
||||||
) {
|
onQuotePress = {
|
||||||
wantsToQuote = baseNote
|
wantsToQuote = baseNote
|
||||||
}
|
},
|
||||||
|
onForkPress = {
|
||||||
|
},
|
||||||
|
)
|
||||||
LikeReaction(
|
LikeReaction(
|
||||||
baseNote = baseNote,
|
baseNote = baseNote,
|
||||||
grayTint = MaterialTheme.colorScheme.onBackground,
|
grayTint = MaterialTheme.colorScheme.onBackground,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
<string name="boost">Boost</string>
|
<string name="boost">Boost</string>
|
||||||
<string name="boosted">boosted</string>
|
<string name="boosted">boosted</string>
|
||||||
<string name="quote">Quote</string>
|
<string name="quote">Quote</string>
|
||||||
|
<string name="fork">Fork</string>
|
||||||
<string name="new_amount_in_sats">New Amount in Sats</string>
|
<string name="new_amount_in_sats">New Amount in Sats</string>
|
||||||
<string name="add">Add</string>
|
<string name="add">Add</string>
|
||||||
<string name="replying_to">"replying to "</string>
|
<string name="replying_to">"replying to "</string>
|
||||||
@@ -777,6 +778,7 @@
|
|||||||
<string name="max_limit">Max Limit</string>
|
<string name="max_limit">Max Limit</string>
|
||||||
<string name="restricted_writes">Restricted Writes</string>
|
<string name="restricted_writes">Restricted Writes</string>
|
||||||
<string name="forked_from">Forked from</string>
|
<string name="forked_from">Forked from</string>
|
||||||
|
<string name="forked_tag">FORK</string>
|
||||||
<string name="git_repository">Git Repository: %1$s</string>
|
<string name="git_repository">Git Repository: %1$s</string>
|
||||||
<string name="git_web_address">Web:</string>
|
<string name="git_web_address">Web:</string>
|
||||||
<string name="git_clone_address">Clone:</string>
|
<string name="git_clone_address">Clone:</string>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.events
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.vitorpamplona.quartz.encoders.ATag
|
||||||
import com.vitorpamplona.quartz.encoders.HexKey
|
import com.vitorpamplona.quartz.encoders.HexKey
|
||||||
import com.vitorpamplona.quartz.encoders.Nip19Bech32
|
import com.vitorpamplona.quartz.encoders.Nip19Bech32
|
||||||
import com.vitorpamplona.quartz.encoders.Nip19Bech32.nip19regex
|
import com.vitorpamplona.quartz.encoders.Nip19Bech32.nip19regex
|
||||||
@@ -42,6 +43,18 @@ open class BaseTextNoteEvent(
|
|||||||
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
|
||||||
fun mentions() = taggedUsers()
|
fun mentions() = taggedUsers()
|
||||||
|
|
||||||
|
fun isAFork() = tags.any { it.size > 3 && (it[0] == "a" || it[0] == "e") && it[3] == "fork" }
|
||||||
|
|
||||||
|
fun forkFromAddress() =
|
||||||
|
tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "fork" }?.let {
|
||||||
|
val aTagValue = it[1]
|
||||||
|
val relay = it.getOrNull(2)
|
||||||
|
|
||||||
|
ATag.parse(aTagValue, relay)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun forkFromVersion() = tags.firstOrNull { it.size > 3 && it[0] == "e" && it[3] == "fork" }?.get(1)
|
||||||
|
|
||||||
open fun replyTos(): List<HexKey> {
|
open fun replyTos(): List<HexKey> {
|
||||||
val oldStylePositional = tags.filter { it.size > 1 && it.size <= 3 && it[0] == "e" }.map { it[1] }
|
val oldStylePositional = tags.filter { it.size > 1 && it.size <= 3 && it[0] == "e" }.map { it[1] }
|
||||||
val newStyleReply = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "reply" }?.get(1)
|
val newStyleReply = tags.lastOrNull { it.size > 3 && it[0] == "e" && it[3] == "reply" }?.get(1)
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class TextNoteEvent(
|
|||||||
directMentions: Set<HexKey> = emptySet(),
|
directMentions: Set<HexKey> = emptySet(),
|
||||||
geohash: String? = null,
|
geohash: String? = null,
|
||||||
nip94attachments: List<FileHeaderEvent>? = null,
|
nip94attachments: List<FileHeaderEvent>? = null,
|
||||||
|
forkedFrom: Event? = null,
|
||||||
signer: NostrSigner,
|
signer: NostrSigner,
|
||||||
createdAt: Long = TimeUtils.now(),
|
createdAt: Long = TimeUtils.now(),
|
||||||
onReady: (TextNoteEvent) -> Unit,
|
onReady: (TextNoteEvent) -> Unit,
|
||||||
@@ -69,6 +70,7 @@ class TextNoteEvent(
|
|||||||
root = root,
|
root = root,
|
||||||
replyingTo = replyingTo,
|
replyingTo = replyingTo,
|
||||||
directMentions = directMentions,
|
directMentions = directMentions,
|
||||||
|
forkedFrom = forkedFrom?.id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -93,6 +95,7 @@ class TextNoteEvent(
|
|||||||
root = root,
|
root = root,
|
||||||
replyingTo = replyingTo,
|
replyingTo = replyingTo,
|
||||||
directMentions = directMentions,
|
directMentions = directMentions,
|
||||||
|
forkedFrom = (forkedFrom as? AddressableEvent)?.address()?.toTag(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -136,6 +139,7 @@ class TextNoteEvent(
|
|||||||
root: String?,
|
root: String?,
|
||||||
replyingTo: String?,
|
replyingTo: String?,
|
||||||
directMentions: Set<HexKey>,
|
directMentions: Set<HexKey>,
|
||||||
|
forkedFrom: String?,
|
||||||
) = sortedWith { o1, o2 ->
|
) = sortedWith { o1, o2 ->
|
||||||
when {
|
when {
|
||||||
o1 == o2 -> 0
|
o1 == o2 -> 0
|
||||||
@@ -150,6 +154,7 @@ class TextNoteEvent(
|
|||||||
when (it) {
|
when (it) {
|
||||||
root -> arrayOf(tagName, it, "", "root")
|
root -> arrayOf(tagName, it, "", "root")
|
||||||
replyingTo -> arrayOf(tagName, it, "", "reply")
|
replyingTo -> arrayOf(tagName, it, "", "reply")
|
||||||
|
forkedFrom -> arrayOf(tagName, it, "", "fork")
|
||||||
in directMentions -> arrayOf(tagName, it, "", "mention")
|
in directMentions -> arrayOf(tagName, it, "", "mention")
|
||||||
else -> arrayOf(tagName, it)
|
else -> arrayOf(tagName, it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,16 +41,6 @@ class WikiNoteEvent(
|
|||||||
|
|
||||||
fun topics() = hashtags()
|
fun topics() = hashtags()
|
||||||
|
|
||||||
fun forkFromAddress() =
|
|
||||||
tags.firstOrNull { it.size > 3 && it[0] == "a" && it[3] == "fork" }?.let {
|
|
||||||
val aTagValue = it[1]
|
|
||||||
val relay = it.getOrNull(2)
|
|
||||||
|
|
||||||
ATag.parse(aTagValue, relay)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun forkFromVersion() = tags.firstOrNull { it.size > 3 && it[0] == "e" && it[3] == "fork" }?.get(1)
|
|
||||||
|
|
||||||
fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1)
|
fun title() = tags.firstOrNull { it.size > 1 && it[0] == "title" }?.get(1)
|
||||||
|
|
||||||
fun summary() = tags.firstOrNull { it.size > 1 && it[0] == "summary" }?.get(1)
|
fun summary() = tags.firstOrNull { it.size > 1 && it[0] == "summary" }?.get(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user