mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-17 04:00:48 +01:00
improved reply text logic
This commit is contained in:
parent
ec6ba7b4e0
commit
318fedf6db
@ -21,42 +21,27 @@
|
||||
|
||||
package org.mariotaku.microblog.library.twitter.model;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.mariotaku.restfu.RestFuUtils;
|
||||
import org.mariotaku.restfu.http.SimpleValueMap;
|
||||
|
||||
public class StatusUpdate extends SimpleValueMap {
|
||||
|
||||
public StatusUpdate(final String status) {
|
||||
public StatusUpdate(@NonNull final String status) {
|
||||
put("status", status);
|
||||
}
|
||||
|
||||
public void setInReplyToStatusId(final String inReplyToStatusId) {
|
||||
put("in_reply_to_status_id", inReplyToStatusId);
|
||||
}
|
||||
|
||||
public void setRepostStatusId(final String repostStatusId) {
|
||||
public StatusUpdate repostStatusId(final String repostStatusId) {
|
||||
put("repost_status_id", repostStatusId);
|
||||
}
|
||||
|
||||
public void setMediaIds(final String... mediaIds) {
|
||||
remove("media_ids");
|
||||
if (mediaIds == null) return;
|
||||
put("media_ids", RestFuUtils.toString(mediaIds, ','));
|
||||
}
|
||||
|
||||
public void setAttachmentUrl(final String attachmentUrl) {
|
||||
put("attachment_url", attachmentUrl);
|
||||
}
|
||||
|
||||
public void setPlaceId(final String placeId) {
|
||||
put("place_id", placeId);
|
||||
}
|
||||
|
||||
public StatusUpdate inReplyToStatusId(final String inReplyToStatusId) {
|
||||
setInReplyToStatusId(inReplyToStatusId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusUpdate inReplyToStatusId(final String inReplyToStatusId) {
|
||||
put("in_reply_to_status_id", inReplyToStatusId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusUpdate displayCoordinates(final boolean displayCoordinates) {
|
||||
put("display_coordinates", displayCoordinates);
|
||||
@ -68,32 +53,42 @@ public class StatusUpdate extends SimpleValueMap {
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusUpdate excludeReplyUserIds(final String[] ids) {
|
||||
put("exclude_reply_userids", RestFuUtils.toString(ids, ','));
|
||||
public StatusUpdate excludeReplyUserIds(@Nullable final String[] ids) {
|
||||
if (ids == null) {
|
||||
remove("exclude_reply_userids");
|
||||
} else {
|
||||
put("exclude_reply_userids", RestFuUtils.toString(ids, ','));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusUpdate location(final GeoLocation location) {
|
||||
remove("lat");
|
||||
remove("long");
|
||||
if (location == null) return this;
|
||||
put("lat", location.getLatitude());
|
||||
put("long", location.getLongitude());
|
||||
if (location == null) {
|
||||
remove("lat");
|
||||
remove("long");
|
||||
} else {
|
||||
put("lat", location.getLatitude());
|
||||
put("long", location.getLongitude());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusUpdate mediaIds(final String... mediaIds) {
|
||||
setMediaIds(mediaIds);
|
||||
public StatusUpdate mediaIds(@Nullable final String[] mediaIds) {
|
||||
if (mediaIds == null) {
|
||||
remove("media_ids");
|
||||
} else {
|
||||
put("media_ids", RestFuUtils.toString(mediaIds, ','));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusUpdate placeId(final String placeId) {
|
||||
setPlaceId(placeId);
|
||||
put("place_id", placeId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusUpdate attachmentUrl(final String attachmentUrl) {
|
||||
setAttachmentUrl(attachmentUrl);
|
||||
put("attachment_url", attachmentUrl);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@ class ExtractorExtensionsKtTest {
|
||||
Assert.assertEquals("lol", it.replyText)
|
||||
Assert.assertTrue("extraMentions.isEmpty()", it.extraMentions.isEmpty())
|
||||
Assert.assertTrue("excludedMentions.isEmpty()", it.excludedMentions.isEmpty())
|
||||
Assert.assertTrue("replyToOriginalUser", it.replyToOriginalUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,6 +69,7 @@ class ExtractorExtensionsKtTest {
|
||||
Assert.assertEquals("@mariotaku lol", it.replyText)
|
||||
Assert.assertTrue("extraMentions.isEmpty()", it.extraMentions.entitiesContainsAll("mariotaku"))
|
||||
Assert.assertTrue("excludedMentions.isEmpty()", it.excludedMentions.isEmpty())
|
||||
Assert.assertTrue("replyToOriginalUser", it.replyToOriginalUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,6 +80,7 @@ class ExtractorExtensionsKtTest {
|
||||
Assert.assertEquals("lol @mariotaku", it.replyText)
|
||||
Assert.assertTrue("extraMentions.isEmpty()", it.extraMentions.entitiesContainsAll("mariotaku"))
|
||||
Assert.assertTrue("excludedMentions.isEmpty()", it.excludedMentions.isEmpty())
|
||||
Assert.assertTrue("replyToOriginalUser", it.replyToOriginalUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +92,7 @@ class ExtractorExtensionsKtTest {
|
||||
Assert.assertTrue("extraMentions.isEmpty()", it.extraMentions.isEmpty())
|
||||
Assert.assertTrue("excludedMentions.containsAll(expectation)",
|
||||
it.excludedMentions.mentionsContainsAll("nixcraft"))
|
||||
Assert.assertTrue("replyToOriginalUser", it.replyToOriginalUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,6 +107,7 @@ class ExtractorExtensionsKtTest {
|
||||
})
|
||||
Assert.assertTrue("excludedMentions.containsAll(expectation)",
|
||||
it.excludedMentions.mentionsContainsAll("nixcraft"))
|
||||
Assert.assertTrue("replyToOriginalUser", it.replyToOriginalUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,6 +120,7 @@ class ExtractorExtensionsKtTest {
|
||||
it.extraMentions.entitiesContainsAll("mariotaku"))
|
||||
Assert.assertTrue("excludedMentions.containsAll(expectation)",
|
||||
it.excludedMentions.mentionsContainsAll("nixcraft"))
|
||||
Assert.assertTrue("replyToOriginalUser", it.replyToOriginalUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,6 +131,7 @@ class ExtractorExtensionsKtTest {
|
||||
Assert.assertEquals("@nixcraft lol", it.replyText)
|
||||
Assert.assertTrue("extraMentions.isEmpty()", it.extraMentions.isEmpty())
|
||||
Assert.assertTrue("excludedMentions.isEmpty()", it.excludedMentions.isEmpty())
|
||||
Assert.assertFalse("replyToOriginalUser", it.replyToOriginalUser)
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,6 +143,7 @@ class ExtractorExtensionsKtTest {
|
||||
Assert.assertTrue("extraMentions.containsAll(expectation)",
|
||||
it.extraMentions.entitiesContainsAll("mariotaku"))
|
||||
Assert.assertTrue("excludedMentions.isEmpty()", it.excludedMentions.isEmpty())
|
||||
Assert.assertFalse("replyToOriginalUser", it.replyToOriginalUser)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,6 @@ import org.mariotaku.twidere.constant.IntentConstants.EXTRA_SCREEN_NAME
|
||||
import org.mariotaku.twidere.extension.applyTheme
|
||||
import org.mariotaku.twidere.extension.loadProfileImage
|
||||
import org.mariotaku.twidere.extension.model.getAccountType
|
||||
import org.mariotaku.twidere.extension.model.getAccountUser
|
||||
import org.mariotaku.twidere.extension.model.textLimit
|
||||
import org.mariotaku.twidere.extension.model.unique_id_non_null
|
||||
import org.mariotaku.twidere.extension.text.twitter.getTweetLength
|
||||
@ -1087,24 +1086,28 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||
private fun handleReplyIntent(status: ParcelableStatus?): Boolean {
|
||||
if (status == null) return false
|
||||
val am = AccountManager.get(this)
|
||||
val accountUser = AccountUtils.findByAccountKey(am, status.account_key)?.getAccountUser(am) ?: return false
|
||||
val details = AccountUtils.getAccountDetails(am, status.account_key, false) ?: return false
|
||||
val accountUser = details.user
|
||||
var selectionStart = 0
|
||||
val mentions = ArrayList<String>()
|
||||
// If replying status from current user, just exclude it's screen name from selection.
|
||||
if (accountUser.key != status.user_key) {
|
||||
if (details.type == AccountType.TWITTER) {
|
||||
// For Twitter, status user should always be the first
|
||||
editText.append("@${status.user_screen_name} ")
|
||||
} else if (accountUser.key != status.user_key) {
|
||||
// If replying status from current user, just exclude it's screen name from selection.
|
||||
editText.append("@${status.user_screen_name} ")
|
||||
selectionStart = editText.length()
|
||||
}
|
||||
selectionStart = editText.length()
|
||||
if (status.is_retweet && !TextUtils.isEmpty(status.retweeted_by_user_screen_name)) {
|
||||
mentions.add(status.retweeted_by_user_screen_name)
|
||||
}
|
||||
if (status.is_quote && !TextUtils.isEmpty(status.quoted_user_screen_name)) {
|
||||
mentions.add(status.quoted_user_screen_name)
|
||||
}
|
||||
if (!ArrayUtils.isEmpty(status.mentions)) {
|
||||
status.mentions
|
||||
.filterNot { it.key == status.account_key || it.screen_name.isNullOrEmpty() }
|
||||
.mapTo(mentions) { it.screen_name }
|
||||
if (status.mentions.isNotNullOrEmpty()) {
|
||||
status.mentions.filterNot {
|
||||
it.key == status.account_key || it.screen_name.isNullOrEmpty()
|
||||
}.mapTo(mentions) { it.screen_name }
|
||||
mentions.addAll(extractor.extractMentionedScreennames(status.quoted_text_plain))
|
||||
} else if (USER_TYPE_FANFOU_COM == status.account_key.host) {
|
||||
addFanfouHtmlToMentions(status.text_unescaped, status.spans, mentions)
|
||||
@ -1119,12 +1122,15 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
|
||||
}
|
||||
|
||||
mentions.distinctBy { it.toLowerCase(Locale.US) }.filterNot {
|
||||
it.equals(status.user_screen_name, ignoreCase = true)
|
||||
|| it.equals(accountUser.screen_name, ignoreCase = true)
|
||||
if (details.type == AccountType.TWITTER && it.equals(accountUser.screen_name,
|
||||
ignoreCase = true)) {
|
||||
return@filterNot true
|
||||
}
|
||||
return@filterNot it.equals(status.user_screen_name, ignoreCase = true)
|
||||
}.forEach { editText.append("@$it ") }
|
||||
|
||||
// Put current user mention at last
|
||||
if (accountUser.key == status.user_key) {
|
||||
// For non-Twitter instances, put current user mention at last
|
||||
if (details.type != AccountType.TWITTER && accountUser.key == status.user_key) {
|
||||
selectionStart = editText.length()
|
||||
editText.append("@${status.user_screen_name} ")
|
||||
}
|
||||
|
@ -38,25 +38,38 @@ fun Extractor.extractMentionsAndNonMentionStartIndex(text: String): MentionsAndN
|
||||
|
||||
fun Extractor.extractReplyTextAndMentions(text: String, inReplyTo: ParcelableStatus): ReplyTextAndMentions {
|
||||
// First extract mentions and 'real text' start index
|
||||
val (mentions, index) = extractMentionsAndNonMentionStartIndex(text)
|
||||
val (textMentions, index) = extractMentionsAndNonMentionStartIndex(text)
|
||||
|
||||
val replyMentions = run {
|
||||
val mentions = inReplyTo.mentions?.toMutableList() ?: mutableListOf()
|
||||
if (inReplyTo.is_retweet) {
|
||||
mentions.add(ParcelableUserMention().also {
|
||||
it.key = inReplyTo.retweeted_by_user_key
|
||||
it.name = inReplyTo.retweeted_by_user_name
|
||||
it.screen_name = inReplyTo.retweeted_by_user_screen_name
|
||||
})
|
||||
}
|
||||
return@run mentions
|
||||
}
|
||||
|
||||
// Find mentions that `inReplyTo` doesn't have and add to `extraMentions` list
|
||||
val extraMentions = mentions.filter { entity ->
|
||||
|
||||
val extraMentions = textMentions.filter { entity ->
|
||||
if (entity.value.equals(inReplyTo.user_screen_name, ignoreCase = true)) {
|
||||
return@filter false
|
||||
}
|
||||
return@filter inReplyTo.mentions?.none { mention ->
|
||||
entity.value.equals(mention.screen_name, ignoreCase = true)
|
||||
} ?: true
|
||||
}
|
||||
// Find removed mentions from `inReplyTo` and add to `excludedMentions` list
|
||||
val excludedMentions = inReplyTo.mentions?.filter { mention ->
|
||||
return@filter mentions.none { entity ->
|
||||
return@filter replyMentions.none { mention ->
|
||||
entity.value.equals(mention.screen_name, ignoreCase = true)
|
||||
}
|
||||
}.orEmpty()
|
||||
}
|
||||
// Find removed mentions from `inReplyTo` and add to `excludedMentions` list
|
||||
val excludedMentions = replyMentions.filter { mention ->
|
||||
return@filter textMentions.none { entity ->
|
||||
entity.value.equals(mention.screen_name, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
// Find reply text contains mention to `inReplyTo.user`
|
||||
val mentioningUser = mentions.any {
|
||||
val mentioningUser = textMentions.any {
|
||||
it.value.equals(inReplyTo.user_screen_name, ignoreCase = true)
|
||||
}
|
||||
if (!mentioningUser) {
|
||||
@ -65,7 +78,7 @@ fun Extractor.extractReplyTextAndMentions(text: String, inReplyTo: ParcelableSta
|
||||
* then this status should be treated at a mention referring to `inReplyTo`, all other mentions
|
||||
* counts.
|
||||
*/
|
||||
return ReplyTextAndMentions(text, emptyList(), emptyList())
|
||||
return ReplyTextAndMentions(text, emptyList(), emptyList(), mentioningUser)
|
||||
}
|
||||
val overrideText = run {
|
||||
val sb = StringBuilder()
|
||||
@ -78,7 +91,7 @@ fun Extractor.extractReplyTextAndMentions(text: String, inReplyTo: ParcelableSta
|
||||
sb.append(text, index, text.length)
|
||||
return@run sb.toString()
|
||||
}
|
||||
return ReplyTextAndMentions(overrideText, extraMentions, excludedMentions)
|
||||
return ReplyTextAndMentions(overrideText, extraMentions, excludedMentions, mentioningUser)
|
||||
}
|
||||
|
||||
data class MentionsAndNonMentionStartIndex(val mentions: List<Extractor.Entity>, val index: Int)
|
||||
@ -86,5 +99,6 @@ data class MentionsAndNonMentionStartIndex(val mentions: List<Extractor.Entity>,
|
||||
data class ReplyTextAndMentions(
|
||||
val replyText: String,
|
||||
val extraMentions: List<Extractor.Entity>,
|
||||
val excludedMentions: List<ParcelableUserMention>
|
||||
val excludedMentions: List<ParcelableUserMention>,
|
||||
val replyToOriginalUser: Boolean
|
||||
)
|
@ -33,7 +33,7 @@ fun Validator.getTweetLength(text: String, ignoreMentions: Boolean, inReplyTo: P
|
||||
return getTweetLength(text)
|
||||
}
|
||||
|
||||
val (replyText, _, _) = InternalExtractor.extractReplyTextAndMentions(text, inReplyTo)
|
||||
val (replyText, _, _, _) = InternalExtractor.extractReplyTextAndMentions(text, inReplyTo)
|
||||
return getTweetLength(replyText)
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ class UpdateStatusTask(
|
||||
* override status text, trim existing reply mentions, and add mentions that not
|
||||
* exists in status text into ignore list
|
||||
*/
|
||||
regulateStatusText(update, pendingUpdate)
|
||||
regulateReplyText(update, pendingUpdate)
|
||||
|
||||
val result: UpdateStatusResult
|
||||
try {
|
||||
@ -146,14 +146,21 @@ class UpdateStatusTask(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun regulateStatusText(update: ParcelableStatusUpdate, pending: PendingStatusUpdate) {
|
||||
private fun regulateReplyText(update: ParcelableStatusUpdate, pending: PendingStatusUpdate) {
|
||||
if (update.draft_action != Draft.Action.REPLY) return
|
||||
val inReplyTo = update.in_reply_to_status ?: return
|
||||
for (i in 0 until pending.length) {
|
||||
if (update.accounts[i].type != AccountType.TWITTER) continue
|
||||
val (replyText, _, excludedMentions) = extractor.extractReplyTextAndMentions(
|
||||
pending.overrideTexts[i], inReplyTo)
|
||||
val (replyText, _, excludedMentions, replyToOriginalUser) =
|
||||
extractor.extractReplyTextAndMentions(pending.overrideTexts[i], inReplyTo)
|
||||
pending.overrideTexts[i] = replyText
|
||||
pending.excludeReplyUserIds[i] = excludedMentions.map { it.key.id }.toTypedArray()
|
||||
pending.replyToOriginalUser[i] = replyToOriginalUser
|
||||
// Fix status to at least make mentioned user know what status it is
|
||||
if (!replyToOriginalUser && update.attachment_url == null) {
|
||||
update.attachment_url = LinkCreator.getTwitterStatusLink(inReplyTo.user_screen_name,
|
||||
inReplyTo.id).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,15 +422,18 @@ class UpdateStatusTask(
|
||||
if (inReplyToStatus != null) {
|
||||
status.inReplyToStatusId(inReplyToStatus.id)
|
||||
if (statusUpdate.accounts[index].type == AccountType.TWITTER) {
|
||||
status.autoPopulateReplyMetadata(true)
|
||||
|
||||
val replyToOriginalUser = pendingUpdate.replyToOriginalUser[index]
|
||||
status.autoPopulateReplyMetadata(replyToOriginalUser)
|
||||
if (replyToOriginalUser) {
|
||||
status.excludeReplyUserIds(pendingUpdate.excludeReplyUserIds[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
if (statusUpdate.repost_status_id != null) {
|
||||
status.setRepostStatusId(statusUpdate.repost_status_id)
|
||||
status.repostStatusId(statusUpdate.repost_status_id)
|
||||
}
|
||||
if (statusUpdate.attachment_url != null) {
|
||||
status.setAttachmentUrl(statusUpdate.attachment_url)
|
||||
status.attachmentUrl(statusUpdate.attachment_url)
|
||||
}
|
||||
if (statusUpdate.location != null) {
|
||||
status.location(ParcelableLocationUtils.toGeoLocation(statusUpdate.location))
|
||||
@ -431,7 +441,7 @@ class UpdateStatusTask(
|
||||
}
|
||||
val mediaIds = pendingUpdate.mediaIds[index]
|
||||
if (mediaIds != null) {
|
||||
status.mediaIds(*mediaIds)
|
||||
status.mediaIds(mediaIds)
|
||||
}
|
||||
if (statusUpdate.is_possibly_sensitive) {
|
||||
status.possiblySensitive(statusUpdate.is_possibly_sensitive)
|
||||
@ -540,6 +550,7 @@ class UpdateStatusTask(
|
||||
|
||||
val overrideTexts: Array<String> = Array(length) { defaultText }
|
||||
val excludeReplyUserIds: Array<Array<String>?> = arrayOfNulls(length)
|
||||
val replyToOriginalUser: BooleanArray = BooleanArray(length)
|
||||
val mediaIds: Array<Array<String>?> = arrayOfNulls(length)
|
||||
|
||||
val mediaUploadResults: Array<MediaUploadResult?> = arrayOfNulls(length)
|
||||
|
Loading…
x
Reference in New Issue
Block a user