diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index 77658b1c5..5cd0754aa 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -47,7 +47,6 @@ import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem; -import org.joinmastodon.android.ui.displayitems.LinkCardStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem; @@ -707,38 +706,22 @@ public abstract class BaseStatusListFragment exten toggleSpoiler(status, isForQuote, holder.getItemID()); } - public void onAddQuoteToStatus(Status status, Status parentStatus) { - int cardIndex=-1; - int textIndex=-1; - int i=0; - for(StatusDisplayItem item:displayItems){ - if(item.parentID.equals(parentStatus.id)){ - if(item instanceof LinkCardStatusDisplayItem){ - cardIndex=i; - }else if(item instanceof TextStatusDisplayItem){ - textIndex=i; - } + public void updateStatusWithQuote(Status status) { + int startIndex=-1; + int endIndex=-1; + for(int i=0; i items=StatusDisplayItem.buildItems(this, status, accountID, parentStatus, knownAccounts, null, flags); - displayItems.remove(cardIndex); - adapter.notifyItemRemoved(cardIndex); - displayItems.addAll(cardIndex, items); - adapter.notifyItemRangeInserted(cardIndex, items.size()); - return; - } - - if (textIndex!=-1) { - ArrayList items=StatusDisplayItem.buildItems(this, status, accountID, parentStatus, knownAccounts, null, flags); - displayItems.addAll(textIndex+1, items); - adapter.notifyItemRangeInserted(textIndex+1, items.size()); + if (startIndex!=-1 && endIndex!=-1) { + ArrayList items=StatusDisplayItem.buildItems(this, status, accountID, status, knownAccounts, null, 0); + displayItems.subList(startIndex, endIndex+1).clear(); + displayItems.addAll(startIndex, items); + adapter.notifyItemRangeChanged(startIndex, items.size()); } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index 0607ede1f..804c9cfce 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -48,6 +48,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -84,6 +85,10 @@ public abstract class StatusDisplayItem{ public static final int FLAG_IS_FOR_QUOTE=1 << 7; public static final int FLAG_NO_MEDIA_PREVIEW=1 << 8; + + private final static Pattern QUOTE_MENTION_PATTERN=Pattern.compile("(?:

)?\\s?(?:RE:\\s?)?]*>https:\\/\\/<\\/span>[^<]+<\\/span>[^<]+<\\/span><\\/a>(?:<\\/p>)?$"); + private final static Pattern QUOTE_PATTERN=Pattern.compile("[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)$"); + public void setAncestryInfo( boolean hasDescendantNeighbor, boolean hasAncestoringNeighbor, @@ -270,6 +275,14 @@ public abstract class StatusDisplayItem{ int quoteInlineIndex=statusForContent.content.lastIndexOf("

RE:"); if(quoteInlineIndex!=-1) statusForContent.content=statusForContent.content.substring(0, quoteInlineIndex); + else { + // hide non-official quote patters + Matcher matcher=QUOTE_MENTION_PATTERN.matcher(status.content); + if(matcher.find()){ + String quoteMention=matcher.group(); + statusForContent.content=statusForContent.content.replace(quoteMention, ""); + } + } } boolean hasSpoiler=!TextUtils.isEmpty(statusForContent.spoilerText); @@ -325,6 +338,8 @@ public abstract class StatusDisplayItem{ if(!statusForContent.mediaAttachments.isEmpty() && statusForContent.poll==null) // add spacing if immediately preceded by attachment contentItems.add(new DummyStatusDisplayItem(parentID, fragment)); contentItems.addAll(buildItems(fragment, statusForContent.quote, accountID, parentObject, knownAccounts, filterContext, FLAG_NO_FOOTER|FLAG_INSET|FLAG_NO_EMOJI_REACTIONS|FLAG_IS_FOR_QUOTE)); + } else { + tryAddNonOfficialQuote(statusForContent, fragment, accountID); } if(contentItems!=items && statusForContent.spoilerRevealed){ items.addAll(contentItems); @@ -378,33 +393,6 @@ public abstract class StatusDisplayItem{ } } - // I actually forgot where I took this, but it works - Pattern pattern = Pattern.compile("[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)"); - Matcher matcher = pattern.matcher(statusForContent.getStrippedText()); - - String lastUrl = null; - while (matcher.find()) { - lastUrl = matcher.group(0); - // The regex doesn't capture the scheme, so I add one here manually, so that the looksLikeFediverseUrlMethod actually works - lastUrl = "https://" + lastUrl; - } - - if (UiUtils.looksLikeFediverseUrl(lastUrl) && statusForContent.quote == null) { - new GetSearchResults(lastUrl, GetSearchResults.Type.STATUSES, true, null, 0, 0).setCallback(new Callback<>(){ - @Override - public void onSuccess(SearchResults results){ - if (!results.statuses.isEmpty()){ - fragment.onAddQuoteToStatus(results.statuses.get(0), statusForContent); - } - } - - @Override - public void onError(ErrorResponse error){ - // Nothing - } - }).exec(accountID); - } - List nonGapItems=gap!=null ? items.subList(0, items.size()-1) : items; WarningFilteredStatusDisplayItem warning=applyingFilter==null ? null : new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, nonGapItems, applyingFilter); @@ -427,6 +415,39 @@ public abstract class StatusDisplayItem{ items.add(new PollFooterStatusDisplayItem(parentID, fragment, poll, status)); } + /** + * Tries to adds a non-official quote to a status. + * A non-official quote is a quote on an instance that does not support quotes officially. + */ + private static void tryAddNonOfficialQuote(Status status, BaseStatusListFragment fragment, String accountID) { + Matcher matcher=QUOTE_PATTERN.matcher(status.getStrippedText()); + + if(!matcher.find()) + return; + String quoteURL="https://"+matcher.group(); + + if (UiUtils.looksLikeFediverseUrl(quoteURL)) { + new GetSearchResults(quoteURL, GetSearchResults.Type.STATUSES, true, null, 0, 0).setCallback(new Callback<>(){ + @Override + public void onSuccess(SearchResults results){ + if (!results.statuses.isEmpty()){ + Status quote=results.statuses.get(0); + // don't show self-referential quotes + if(!Objects.equals(status.id, results.statuses.get(0).id)){ + status.quote=quote; + fragment.updateStatusWithQuote(status); + } + } + } + + @Override + public void onError(ErrorResponse error){ + Log.w("StatusDisplayItem", "onError: failed to find quote status with URL: " + quoteURL + " " + error); + } + }).exec(accountID); + } + } + public enum Type{ HEADER, REBLOG_OR_REPLY_LINE,