Add Mastodon Quote support in TL

This commit is contained in:
Thomas 2025-07-24 15:52:24 +02:00
parent 79a8dee7ab
commit ca9c47edea
4 changed files with 61 additions and 14 deletions

View file

@ -16,12 +16,15 @@ package app.fedilab.android.mastodon.client.entities.api;
import android.content.Context;
import android.text.Spannable;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
@ -29,7 +32,6 @@ import java.lang.ref.WeakReference;
import java.util.Date;
import java.util.List;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.SpannableHelper;
import de.timfreiheit.mathjax.android.MathJaxView;
@ -84,7 +86,7 @@ public class Status implements Serializable, Cloneable {
@SerializedName("reblog")
public Status reblog;
@SerializedName("quote")
public Status quote;
private Object quote;
@SerializedName("application")
public App application;
@SerializedName("account")
@ -114,6 +116,35 @@ public class Status implements Serializable, Cloneable {
@SerializedName("reactions")
public List<Reaction> reactions;
public Status getQuote() {
Status quote = null;
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = String.valueOf(this.quote);
try {
json = ow.writeValueAsString(this.quote);
} catch (JsonProcessingException ignored) {
}
Gson gson = new Gson();
try{
quote = gson.fromJson(json, Status.class);
if(quote.account == null) {
MastodonQuote mastodonQuote = gson.fromJson(json, MastodonQuote.class);
if(mastodonQuote.quoted_status != null && (mastodonQuote.state != null && mastodonQuote.state.equals("accepted"))) {
quote = mastodonQuote.quoted_status;
}
}
} catch (Exception e) {
e.printStackTrace();
}
if(quote !=null && quote.account != null) {
return quote;
}
return null;
}
public void setQuote(Status quote) {
this.quote =quote;
}
public String attachedNotification = null;
public int gifPosition = 0;
@ -202,4 +233,11 @@ public class Status implements Serializable, Cloneable {
void emojiFetched();
}
private static class MastodonQuote implements Serializable {
@SerializedName("state")
String state;
@SerializedName("quoted_status")
Status quoted_status;
}
}

View file

@ -571,7 +571,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else {
holder.binding.pronouns.setVisibility(View.GONE);
}
if (statusToDeal.quote != null && (statusToDeal.spoiler_text == null || statusToDeal.spoiler_text.trim().isEmpty() || statusToDeal.isExpended)) {
if (statusToDeal.getQuote() != null && (statusToDeal.spoiler_text == null || statusToDeal.spoiler_text.trim().isEmpty() || statusToDeal.isExpended)) {
holder.binding.quotedMessage.cardviewContainer.setCardElevation((int) Helper.convertDpToPixel(5, context));
holder.binding.quotedMessage.dividerCard.setVisibility(View.GONE);
holder.binding.quotedMessage.cardviewContainer.setStrokeWidth((int) Helper.convertDpToPixel(1, context));
@ -595,7 +595,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (!remote) {
Intent intent = new Intent(context, ContextActivity.class);
Bundle args = new Bundle();
args.putSerializable(Helper.ARG_STATUS, statusToDeal.quote);
args.putSerializable(Helper.ARG_STATUS, statusToDeal.getQuote());
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> {
Bundle bundle = new Bundle();
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
@ -603,27 +603,27 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
context.startActivity(intent);
});
} else {
Helper.openBrowser(context,statusToDeal.quote.url);
Helper.openBrowser(context,statusToDeal.getQuote().url);
}
});
holder.binding.quotedMessage.cardviewContainer.setStrokeColor(ThemeHelper.getAttColor(context, R.attr.colorPrimary));
holder.binding.quotedMessage.statusContent.setText(
statusToDeal.quote.getSpanContent(context, remote,
statusToDeal.getQuote().getSpanContent(context, remote,
new WeakReference<>(holder.binding.quotedMessage.statusContent), null),
TextView.BufferType.SPANNABLE);
MastodonHelper.loadPPMastodon(holder.binding.quotedMessage.avatar, statusToDeal.quote.account);
if (statusToDeal.quote.account != null) {
MastodonHelper.loadPPMastodon(holder.binding.quotedMessage.avatar, statusToDeal.getQuote().account);
if (statusToDeal.getQuote().account != null) {
holder.binding.quotedMessage.displayName.setText(
statusToDeal.quote.account.getSpanDisplayName(context,
statusToDeal.getQuote().account.getSpanDisplayName(context,
new WeakReference<>(holder.binding.quotedMessage.displayName)),
TextView.BufferType.SPANNABLE);
holder.binding.quotedMessage.username.setText(String.format("@%s", statusToDeal.quote.account.acct));
holder.binding.quotedMessage.username.setText(String.format("@%s", statusToDeal.getQuote().account.acct));
}
if (statusToDeal.quote.spoiler_text != null && !statusToDeal.quote.spoiler_text.trim().isEmpty()) {
if (statusToDeal.getQuote().spoiler_text != null && !statusToDeal.getQuote().spoiler_text.trim().isEmpty()) {
holder.binding.quotedMessage.spoiler.setVisibility(View.VISIBLE);
holder.binding.quotedMessage.spoiler.setText(
statusToDeal.quote.getSpanSpoiler(context,
statusToDeal.getQuote().getSpanSpoiler(context,
new WeakReference<>(holder.binding.quotedMessage.spoiler), null),
TextView.BufferType.SPANNABLE);
} else {

View file

@ -381,7 +381,7 @@ public class TimelinesVM extends AndroidViewModel {
//Quoted message
if(!timelineItem.select(".quote").html().isEmpty()) {
status.quote = Nitter.nitterHTMLParser(context, timelineItem.select(".quote").first(), nitterInstance);
status.setQuote(Nitter.nitterHTMLParser(context, timelineItem.select(".quote").first(), nitterInstance));
}
Status finalStatus;

View file

@ -0,0 +1,9 @@
Added:
Changed:
Fixed:
- Fix a crash with threads
- Fix empty Hashtags