Hashtag timeline

This commit is contained in:
Grishka 2022-03-02 22:39:59 +03:00
parent 9f0b55918d
commit b437f6f3a3
11 changed files with 86 additions and 14 deletions

View File

@ -10,7 +10,7 @@ android {
applicationId "org.joinmastodon.android"
minSdk 23
targetSdk 31
versionCode 4
versionCode 5
versionName "0.1"
}

View File

@ -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<List<Status>>{
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);
}
}

View File

@ -270,7 +270,7 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
if(!TextUtils.isEmpty(account.header))
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), accountID);
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
if(account.emojis.isEmpty()){
parsedName=account.displayName;
}else{

View File

@ -0,0 +1,40 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
import org.joinmastodon.android.model.Status;
import java.util.List;
import me.grishka.appkit.api.SimpleCallback;
public class HashtagTimelineFragment extends StatusListFragment{
private String hashtag;
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
hashtag=getArguments().getString("hashtag");
setTitle('#'+hashtag);
}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetHashtagTimeline(hashtag, offset==0 ? null : getMaxID(), null, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
onDataLoaded(result, !result.isEmpty());
}
})
.exec(accountID);
}
@Override
protected void onShown(){
super.onShown();
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
loadData();
}
}

View File

@ -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);

View File

@ -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<Status>{
@Subscribe
public void onStatusDeleted(StatusDeletedEvent ev){
Log.i("11", "on status deleted!");
if(!ev.accountID.equals(accountID))
return;
Status status=getStatusByID(ev.id);

View File

@ -101,7 +101,7 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag>{
@Override
public void onClick(){
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name);
}
}
}

View File

@ -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<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());

View File

@ -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>:code:</code>
* @return a spanned string
*/
public static SpannableStringBuilder parse(String source, List<Emoji> emojis, List<Mention> mentions, String accountID){
public static SpannableStringBuilder parse(String source, List<Emoji> emojis, List<Mention> mentions, List<Hashtag> tags, String accountID){
class SpanInfo{
public Object span;
public int start;
@ -57,6 +58,8 @@ public class HtmlParser{
}
Map<String, String> 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<String, String> 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){

View File

@ -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);
}
}

View File

@ -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);
}