From b437f6f3a383ce2989656607b7d9aae8bb2ef341 Mon Sep 17 00:00:00 2001 From: Grishka Date: Wed, 2 Mar 2022 22:39:59 +0300 Subject: [PATCH] Hashtag timeline --- mastodon/build.gradle | 2 +- .../timelines/GetHashtagTimeline.java | 20 ++++++++++ .../fragments/DiscoverAccountsFragment.java | 2 +- .../fragments/HashtagTimelineFragment.java | 40 +++++++++++++++++++ .../android/fragments/ProfileFragment.java | 4 +- .../android/fragments/StatusListFragment.java | 4 -- .../fragments/TrendingHashtagsFragment.java | 2 +- .../ui/displayitems/StatusDisplayItem.java | 2 +- .../android/ui/text/HtmlParser.java | 13 +++++- .../android/ui/text/LinkSpan.java | 3 +- .../android/ui/utils/UiUtils.java | 8 ++++ 11 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/api/requests/timelines/GetHashtagTimeline.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java diff --git a/mastodon/build.gradle b/mastodon/build.gradle index 220bfbda..16668ac6 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -10,7 +10,7 @@ android { applicationId "org.joinmastodon.android" minSdk 23 targetSdk 31 - versionCode 4 + versionCode 5 versionName "0.1" } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/timelines/GetHashtagTimeline.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/timelines/GetHashtagTimeline.java new file mode 100644 index 00000000..4a3c831d --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/timelines/GetHashtagTimeline.java @@ -0,0 +1,20 @@ +package org.joinmastodon.android.api.requests.timelines; + +import com.google.gson.reflect.TypeToken; + +import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.model.Status; + +import java.util.List; + +public class GetHashtagTimeline extends MastodonAPIRequest>{ + public GetHashtagTimeline(String hashtag, String maxID, String minID, int limit){ + super(HttpMethod.GET, "/timelines/tag/"+hashtag, new TypeToken<>(){}); + if(maxID!=null) + addQueryParameter("max_id", maxID); + if(minID!=null) + addQueryParameter("min_id", minID); + if(limit>0) + addQueryParameter("limit", ""+limit); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/DiscoverAccountsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/DiscoverAccountsFragment.java index efe52fd0..e5942b39 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/DiscoverAccountsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/DiscoverAccountsFragment.java @@ -270,7 +270,7 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment(this){ + @Override + public void onSuccess(List result){ + onDataLoaded(result, !result.isEmpty()); + } + }) + .exec(accountID); + } + + @Override + protected void onShown(){ + super.onShown(); + if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading) + loadData(); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index db469bed..3b852cbf 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -375,7 +375,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList name.setText(ssb); setTitle(ssb); username.setText('@'+account.acct); - bio.setText(HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), accountID)); + bio.setText(HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID)); followersCount.setText(UiUtils.abbreviateNumber(account.followersCount)); followingCount.setText(UiUtils.abbreviateNumber(account.followingCount)); postsCount.setText(UiUtils.abbreviateNumber(account.statusesCount)); @@ -400,7 +400,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList fields.add(joined); for(AccountField field:account.fields){ - field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), accountID); + field.parsedValue=ssb=HtmlParser.parse(field.value, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID); field.valueEmojis=ssb.getSpans(0, ssb.length(), CustomEmojiSpan.class); ssb=new SpannableStringBuilder(field.name); HtmlParser.parseCustomEmoji(ssb, account.emojis); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java index b4c802a7..bdc07760 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java @@ -1,7 +1,6 @@ package org.joinmastodon.android.fragments; import android.os.Bundle; -import android.util.Log; import com.squareup.otto.Subscribe; @@ -12,8 +11,6 @@ import org.joinmastodon.android.model.Poll; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem; -import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem; -import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.StatusDisplayItem; import org.parceler.Parcels; @@ -92,7 +89,6 @@ public abstract class StatusListFragment extends BaseStatusListFragment{ @Subscribe public void onStatusDeleted(StatusDeletedEvent ev){ - Log.i("11", "on status deleted!"); if(!ev.accountID.equals(accountID)) return; Status status=getStatusByID(ev.id); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/TrendingHashtagsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/TrendingHashtagsFragment.java index a20a82c6..93eecf09 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/TrendingHashtagsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/TrendingHashtagsFragment.java @@ -101,7 +101,7 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment{ @Override public void onClick(){ - + UiUtils.openHashtagTimeline(getActivity(), accountID, item.name); } } } 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 25a8ac5c..33cbd031 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 @@ -74,7 +74,7 @@ public abstract class StatusDisplayItem{ HeaderStatusDisplayItem header; items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent)); if(!TextUtils.isEmpty(statusForContent.content)) - items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, accountID), fragment, statusForContent)); + items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent)); else header.needBottomPadding=true; List imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList()); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java index 21a324c9..102a2962 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java @@ -5,6 +5,7 @@ import android.text.Spanned; import android.widget.TextView; import org.joinmastodon.android.model.Emoji; +import org.joinmastodon.android.model.Hashtag; import org.joinmastodon.android.model.Mention; import org.joinmastodon.android.ui.utils.UiUtils; import org.jsoup.Jsoup; @@ -43,7 +44,7 @@ public class HtmlParser{ * @param emojis Custom emojis that are present in source as :code: * @return a spanned string */ - public static SpannableStringBuilder parse(String source, List emojis, List mentions, String accountID){ + public static SpannableStringBuilder parse(String source, List emojis, List mentions, List tags, String accountID){ class SpanInfo{ public Object span; public int start; @@ -57,6 +58,8 @@ public class HtmlParser{ } Map idsByUrl=mentions.stream().collect(Collectors.toMap(m->m.url, m->m.id)); + // Hashtags in remote posts have remote URLs, these have local URLs so they don't match. +// Map tagsByUrl=tags.stream().collect(Collectors.toMap(t->t.url, t->t.name)); final SpannableStringBuilder ssb=new SpannableStringBuilder(); Jsoup.parseBodyFragment(source).body().traverse(new NodeVisitor(){ @@ -73,7 +76,13 @@ public class HtmlParser{ String href=el.attr("href"); LinkSpan.Type linkType; if(el.hasClass("hashtag")){ - linkType=LinkSpan.Type.HASHTAG; + String text=el.text(); + if(text.startsWith("#")){ + linkType=LinkSpan.Type.HASHTAG; + href=text.substring(1); + }else{ + linkType=LinkSpan.Type.URL; + } }else if(el.hasClass("mention")){ String id=idsByUrl.get(href); if(id!=null){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/LinkSpan.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/LinkSpan.java index efd9aabf..0b3ac7f9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/LinkSpan.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/LinkSpan.java @@ -3,7 +3,6 @@ package org.joinmastodon.android.ui.text; import android.content.Context; import android.text.TextPaint; import android.text.style.CharacterStyle; -import android.widget.Toast; import org.joinmastodon.android.ui.utils.UiUtils; @@ -35,7 +34,7 @@ public class LinkSpan extends CharacterStyle { switch(getType()){ case URL -> UiUtils.launchWebBrowser(context, link); case MENTION -> UiUtils.openProfileByID(context, accountID, link); - case HASHTAG -> Toast.makeText(context, "Not implemented yet", Toast.LENGTH_SHORT).show(); + case HASHTAG -> UiUtils.openHashtagTimeline(context, accountID, link); } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index a2092580..8d684e60 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -24,6 +24,7 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed; import org.joinmastodon.android.api.requests.accounts.SetAccountMuted; import org.joinmastodon.android.api.requests.statuses.DeleteStatus; import org.joinmastodon.android.events.StatusDeletedEvent; +import org.joinmastodon.android.fragments.HashtagTimelineFragment; import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Emoji; @@ -186,6 +187,13 @@ public class UiUtils{ Nav.go((Activity)context, ProfileFragment.class, args); } + public static void openHashtagTimeline(Context context, String accountID, String hashtag){ + Bundle args=new Bundle(); + args.putString("account", accountID); + args.putString("hashtag", hashtag); + Nav.go((Activity)context, HashtagTimelineFragment.class, args); + } + public static void showConfirmationAlert(Context context, @StringRes int title, @StringRes int message, @StringRes int confirmButton, Runnable onConfirmed){ showConfirmationAlert(context, context.getString(title), context.getString(message), context.getString(confirmButton), onConfirmed); }