diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/WriteNewNoteScreen.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/WriteNewNoteScreen.kt index 88de27ab..141cc2fd 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/WriteNewNoteScreen.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/WriteNewNoteScreen.kt @@ -2,19 +2,20 @@ package ac.cord.auxiliary.compose.ui.composable import ac.cord.auxiliary.compose.database.model.NostrEvent import ac.cord.auxiliary.compose.database.model.Profile +import ac.cord.auxiliary.compose.database.model.intermdiate.LocalMention import ac.cord.auxiliary.compose.database.model.intermdiate.LocalNostrEvent import ac.cord.auxiliary.compose.database.model.intermdiate.LocalProfileWithFollowing import ac.cord.auxiliary.compose.extensions.toFormattedTimeAndDateString import ac.cord.auxiliary.compose.repository.NostrRepository import ac.cord.auxiliary.compose.ui.composable.widgets.LoadingDataIndicator -import ac.cord.auxiliary.compose.ui.composable.widgets.detail.TextNoteEventDetail +import ac.cord.auxiliary.compose.ui.composable.widgets.content.ContentParser +import ac.cord.auxiliary.compose.ui.composable.widgets.content.ContentSegment import ac.cord.auxiliary.compose.ui.composable.widgets.profile.ProfileAvatar import ac.cord.auxiliary.compose.ui.theme.AuxTheme import ac.cord.auxiliary.compose.ui.view.model.WriteNewNoteViewModel import ac.cord.auxiliary.compose.ui.view.state.WriteNewNoteUIState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -23,16 +24,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.input.OutputTransformation import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Create -import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.NavigateNext import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.LoadingIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -46,14 +45,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType 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.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent import kotlin.time.Clock -import kotlin.time.Instant @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable @@ -126,6 +123,8 @@ fun WriteNewNoteScreen( ) } + val mentionedProfiles: List = emptyList() + TextField( modifier = Modifier.fillMaxWidth(), state = writeNewNoteViewModel.writeNewNoteFormState.textField.textFieldState, @@ -166,6 +165,26 @@ fun WriteNewNoteScreen( profile = writeNewNoteUIState.localProfile.profile, size = 35.dp ) + }, + outputTransformation = OutputTransformation { + val content = originalText.toString() + val nostrProfiles = ContentParser.parse(content.trimEnd('\n', '\r'), emptyMap(), emptyMap(), trimBlankLines = true) + .filterIsInstance() + // TODO: We might want to order these profiles by the startIndex so that we can replace the better + + if (mentionedProfiles.isNotEmpty()) { + for (nostrProfile in nostrProfiles) { + mentionedProfiles.find { mentionedProfile -> mentionedProfile.profile?.publicKey == nostrProfile.pubkey }?.let { mentionedProfile -> + val profileMention = mentionedProfile.profile?.humanReadableNameOrPubkey() ?: "${nostrProfile.pubkey.take(8)}...${nostrProfile.pubkey.takeLast(4)}" + + replace( + start = nostrProfile.startIndex, + end = nostrProfile.endIndex, + text = "@$profileMention" + ) + } + } + } } ) diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/ContentParser.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/ContentParser.kt index 54d6eab6..69a7e099 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/ContentParser.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/ContentParser.kt @@ -55,16 +55,30 @@ internal object ContentParser { else -> segments.add(ContentSegment.InlineLinkSegment(url)) } }else if (nip19ParseResult.isNotEmpty()) { + Logger.d("Range: ${match.range}") Logger.d("Nip19Result: $nip19ParseResult") when (val entity = nip19ParseResult.first()) { is NAddress -> { segments.add(ContentSegment.NostrAddressableSegment(entity.dTag, entity.relay.map { it.url }, entity.author, entity.kind)) } is NProfile -> { - segments.add(ContentSegment.NostrProfileSegment(entity.hex, entity.relay.map { it.url })) + segments.add( + ContentSegment.NostrProfileSegment( + pubkey = entity.hex, + relayHints = entity.relay.map { it.url }, + startIndex = match.range.first, + endIndex = match.range.last + ) + ) } is NPub -> { - segments.add(ContentSegment.NostrProfileSegment(pubkey = entity.hex)) + segments.add( + ContentSegment.NostrProfileSegment( + pubkey = entity.hex, + startIndex = match.range.first, + endIndex = match.range.last + ) + ) } is NNote -> { segments.add(ContentSegment.NostrNoteSegment(entity.hex)) diff --git a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/ContentSegment.kt b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/ContentSegment.kt index cbfb4352..62bb6c4d 100644 --- a/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/ContentSegment.kt +++ b/composeApp/src/commonMain/kotlin/ac/cord/auxiliary/compose/ui/composable/widgets/content/ContentSegment.kt @@ -53,7 +53,12 @@ internal sealed interface ContentSegment { ) } } - data class NostrProfileSegment(val pubkey: String, val relayHints: List = emptyList()) : ContentSegment { + data class NostrProfileSegment( + val pubkey: String, + val relayHints: List = emptyList(), + val startIndex: Int, + val endIndex: Int + ) : ContentSegment { override fun toString(): String { return NProfile.create( authorPubKeyHex = pubkey,