Quote Note support

pull/147/head
Vitor Pamplona 2023-02-20 17:28:58 -05:00
rodzic 1f2e120851
commit dbcd5ed7fe
4 zmienionych plików z 129 dodań i 23 usunięć

Wyświetl plik

@ -41,6 +41,8 @@ fun decodePublicKey(key: String): ByteArray {
Persona(privKey = key.bechToBytes()).pubKey
} else if (key.startsWith("npub")) {
key.bechToBytes()
} else if (key.startsWith("note")) {
key.bechToBytes()
} else { //if (pattern.matcher(key).matches()) {
//} else {
Hex.decode(key)

Wyświetl plik

@ -45,11 +45,9 @@ import nostr.postr.events.TextNoteEvent
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, account: Account) {
fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, quote: Note? = null, account: Account) {
val postViewModel: NewPostViewModel = viewModel()
postViewModel.load(account, baseReplyTo)
val context = LocalContext.current
// initialize focus reference to be able to request focus programmatically
@ -59,6 +57,7 @@ fun NewPostView(onClose: () -> Unit, baseReplyTo: Note? = null, account: Account
val scroolState = rememberScrollState()
LaunchedEffect(Unit) {
postViewModel.load(account, baseReplyTo, quote)
delay(100)
focusRequester.requestFocus()

Wyświetl plik

@ -31,7 +31,7 @@ class NewPostViewModel: ViewModel() {
var userSuggestions by mutableStateOf<List<User>>(emptyList())
var userSuggestionAnchor: TextRange? = null
fun load(account: Account, replyingTo: Note?) {
fun load(account: Account, replyingTo: Note?, quote: Note?) {
originalNote = replyingTo
replyingTo?.let { replyNote ->
this.replyTos = (replyNote.replyTo ?: emptyList()).plus(replyNote)
@ -45,29 +45,60 @@ class NewPostViewModel: ViewModel() {
}
}
quote?.let {
message = TextFieldValue(message.text + "\n\n@${it.idNote()}")
}
this.account = account
}
fun addUserToMentionsIfNotInAndReturnIndex(user: User): Int {
val replyToSize = replyTos?.size ?: 0
fun addUserToMentions(user: User) {
mentions = if (mentions?.contains(user) == true) mentions else mentions?.plus(user) ?: listOf(user)
}
var myMentions = mentions
if (myMentions == null) {
mentions = listOf(user)
return replyToSize + 0 // position of the user
}
fun addNoteToReplyTos(note: Note) {
note.author?.let { addUserToMentions(it) }
replyTos = if (replyTos?.contains(note) == true) replyTos else replyTos?.plus(note) ?: listOf(note)
}
val index = myMentions.indexOf(user)
fun tagIndex(user: User): Int {
// Postr Events assembles replies before mentions in the tag order
return (replyTos?.size ?: 0) + (mentions?.indexOf(user) ?: 0)
}
if (index >= 0) return replyToSize + index
myMentions = myMentions.plus(user)
mentions = myMentions
return replyToSize + myMentions.indexOf(user)
fun tagIndex(note: Note): Int {
// Postr Events assembles replies before mentions in the tag order
return (replyTos?.indexOf(note) ?: 0)
}
fun sendPost() {
// Moves @npub to mentions
// adds all references to mentions and reply tos
message.text.split('\n').forEach { paragraph: String ->
paragraph.split(' ').forEach { word: String ->
try {
if (word.startsWith("@npub") && word.length >= 64) {
val keyB32 = word.substring(0, 64)
val key = decodePublicKey(keyB32.removePrefix("@"))
val user = LocalCache.getOrCreateUser(key.toHexKey())
addUserToMentions(user)
} else if (word.startsWith("@note") && word.length >= 64) {
val keyB32 = word.substring(0, 64)
val key = decodePublicKey(keyB32.removePrefix("@"))
val note = LocalCache.getOrCreateNote(key.toHexKey())
addNoteToReplyTos(note)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
// Tags the text in the correct order.
val newMessage = message.text.split('\n').map { paragraph: String ->
paragraph.split(' ').map { word: String ->
try {
@ -78,15 +109,20 @@ class NewPostViewModel: ViewModel() {
val key = decodePublicKey(keyB32.removePrefix("@"))
val user = LocalCache.getOrCreateUser(key.toHexKey())
val index = addUserToMentionsIfNotInAndReturnIndex(user)
"#[${tagIndex(user)}]$restOfWord"
} else if (word.startsWith("@note") && word.length >= 64) {
val keyB32 = word.substring(0, 64)
val restOfWord = word.substring(64)
val newWord = "#[${index}]"
val key = decodePublicKey(keyB32.removePrefix("@"))
val note = LocalCache.getOrCreateNote(key.toHexKey())
newWord + restOfWord
"#[${tagIndex(note)}]$restOfWord"
} else {
word
}
} catch (e: Exception) {
e.printStackTrace()
// if it can't parse the key, don't try to change.
word
}

Wyświetl plik

@ -109,12 +109,21 @@ fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel) {
mutableStateOf<Note?>(null)
}
var wantsToQuote by remember {
mutableStateOf<Note?>(null)
}
if (wantsToReplyTo != null)
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, account)
NewPostView({ wantsToReplyTo = null }, wantsToReplyTo, null, account)
if (wantsToQuote != null)
NewPostView({ wantsToQuote = null }, null, wantsToQuote, account)
var wantsToZap by remember { mutableStateOf(false) }
var wantsToChangeZapAmount by remember { mutableStateOf(false) }
var wantsToBoost by remember { mutableStateOf(false) }
Row(
modifier = Modifier
.padding(top = 8.dp)
@ -156,7 +165,7 @@ fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel) {
modifier = Modifier.then(Modifier.size(20.dp)),
onClick = {
if (account.isWriteable())
accountViewModel.boost(baseNote)
wantsToBoost = true
else
scope.launch {
Toast.makeText(
@ -167,6 +176,20 @@ fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel) {
}
}
) {
if (wantsToBoost) {
BoostTypeChoicePopup(
baseNote,
accountViewModel,
onDismiss = {
wantsToBoost = false
},
onQuote = {
wantsToBoost = false
wantsToQuote = baseNote
}
)
}
if (boostedNote?.isBoostedBy(account.userProfile()) == true) {
Icon(
painter = painterResource(R.drawable.ic_retweeted),
@ -345,6 +368,52 @@ fun ReactionsRow(baseNote: Note, accountViewModel: AccountViewModel) {
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun BoostTypeChoicePopup(baseNote: Note, accountViewModel: AccountViewModel, onDismiss: () -> Unit, onQuote: () -> Unit) {
val scope = rememberCoroutineScope()
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
Popup(
alignment = Alignment.BottomCenter,
offset = IntOffset(0, -50),
onDismissRequest = { onDismiss() }
) {
FlowRow() {
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = {
accountViewModel.boost(baseNote)
onDismiss()
},
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
)
) {
Text("Boost", color = Color.White, textAlign = TextAlign.Center)
}
Button(
modifier = Modifier.padding(horizontal = 3.dp),
onClick = onQuote,
shape = RoundedCornerShape(20.dp),
colors = ButtonDefaults
.buttonColors(
backgroundColor = MaterialTheme.colors.primary
)
) {
Text("Quote", color = Color.White, textAlign = TextAlign.Center)
}
}
}
}
@OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
@Composable
private fun ZapAmountChoicePopup(baseNote: Note, accountViewModel: AccountViewModel, onDismiss: () -> Unit, onChangeAmount: () -> Unit) {