Featured tab in profiles
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class FeaturedHashtagsListFragment extends BaseStatusListFragment<Hashtag>{
|
||||
private Account account;
|
||||
private String accountID;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
accountID=getArguments().getString("account");
|
||||
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
||||
onDataLoaded(getArguments().getParcelableArrayList("hashtags").stream().map(p->(Hashtag)Parcels.unwrap(p)).collect(Collectors.toList()), false);
|
||||
setTitle(R.string.featured_hashtags);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<StatusDisplayItem> buildDisplayItems(Hashtag s){
|
||||
return Collections.singletonList(new HashtagStatusDisplayItem(s.name, this, s));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAccountToKnown(Hashtag s){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(String id){
|
||||
UiUtils.openHashtagTimeline(getActivity(), accountID, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){}
|
||||
|
||||
@Override
|
||||
protected void drawDivider(View child, View bottomSibling, RecyclerView.ViewHolder holder, RecyclerView.ViewHolder siblingHolder, RecyclerView parent, Canvas c, Paint paint){
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
|
||||
public class PinnedPostsListFragment extends StatusListFragment{
|
||||
private Account account;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
account=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
||||
setTitle(R.string.pinned_posts);
|
||||
loadData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
new GetAccountStatuses(account.id, null, null, 100, GetAccountStatuses.Filter.PINNED)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
onDataLoaded(result, false);
|
||||
}
|
||||
}).exec(accountID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.view.View;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountFeaturedHashtags;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.SearchResult;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.SectionHeaderStatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
|
||||
public class ProfileFeaturedFragment extends BaseStatusListFragment<SearchResult>{
|
||||
private Account profileAccount;
|
||||
private List<Hashtag> featuredTags;
|
||||
// private List<Account> endorsedAccounts;
|
||||
private List<Status> pinnedStatuses;
|
||||
private boolean tagsLoaded, statusesLoaded;
|
||||
|
||||
public ProfileFeaturedFragment(){
|
||||
setListLayoutId(R.layout.recycler_fragment_no_refresh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
profileAccount=Parcels.unwrap(getArguments().getParcelable("profileAccount"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<StatusDisplayItem> buildDisplayItems(SearchResult s){
|
||||
ArrayList<StatusDisplayItem> items=switch(s.type){
|
||||
case ACCOUNT -> new ArrayList<>(Collections.singletonList(new AccountStatusDisplayItem(s.id, this, s.account)));
|
||||
case HASHTAG -> new ArrayList<>(Collections.singletonList(new HashtagStatusDisplayItem(s.id, this, s.hashtag)));
|
||||
case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, false, true);
|
||||
};
|
||||
|
||||
if(s.firstInSection){
|
||||
items.add(0, new SectionHeaderStatusDisplayItem(this, getString(switch(s.type){
|
||||
case ACCOUNT -> R.string.profile_endorsed_accounts;
|
||||
case HASHTAG -> R.string.hashtags;
|
||||
case STATUS -> R.string.posts;
|
||||
}), getString(R.string.view_all), switch(s.type){
|
||||
case ACCOUNT -> (Runnable)this::showAllEndorsedAccounts;
|
||||
case HASHTAG -> (Runnable)this::showAllFeaturedHashtags;
|
||||
case STATUS -> (Runnable)this::showAllPinnedPosts;
|
||||
}));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAccountToKnown(SearchResult s){
|
||||
Account acc=switch(s.type){
|
||||
case ACCOUNT -> s.account;
|
||||
case STATUS -> s.status.account;
|
||||
case HASHTAG -> null;
|
||||
};
|
||||
if(acc!=null && !knownAccounts.containsKey(acc.id))
|
||||
knownAccounts.put(acc.id, acc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(String id){
|
||||
SearchResult res=getResultByID(id);
|
||||
if(res==null)
|
||||
return;
|
||||
switch(res.type){
|
||||
case ACCOUNT -> {
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("profileAccount", Parcels.wrap(res.account));
|
||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||
}
|
||||
case HASHTAG -> UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name);
|
||||
case STATUS -> {
|
||||
Status status=res.status.getContentStatus();
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("status", Parcels.wrap(status));
|
||||
if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId))
|
||||
args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId)));
|
||||
Nav.go(getActivity(), ThreadFragment.class, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
if(!statusesLoaded){
|
||||
new GetAccountStatuses(profileAccount.id, null, null, 1, GetAccountStatuses.Filter.PINNED)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(List<Status> result){
|
||||
pinnedStatuses=result;
|
||||
statusesLoaded=true;
|
||||
onOneApiRequestCompleted();
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
if(!tagsLoaded){
|
||||
new GetAccountFeaturedHashtags(profileAccount.id)
|
||||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(List<Hashtag> result){
|
||||
featuredTags=result;
|
||||
tagsLoaded=true;
|
||||
onOneApiRequestCompleted();
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShown(){
|
||||
super.onShown();
|
||||
if(!getArguments().getBoolean("noAutoLoad") && !loaded && !dataLoading)
|
||||
loadData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh(){
|
||||
statusesLoaded=false;
|
||||
tagsLoaded=false;
|
||||
super.onRefresh();
|
||||
}
|
||||
|
||||
private void onOneApiRequestCompleted(){
|
||||
if(tagsLoaded && statusesLoaded){
|
||||
ArrayList<SearchResult> results=new ArrayList<>();
|
||||
if(!pinnedStatuses.isEmpty()){
|
||||
SearchResult res=new SearchResult(pinnedStatuses.get(0));
|
||||
res.firstInSection=true;
|
||||
results.add(res);
|
||||
}
|
||||
for(int i=0;i<Math.min(3, featuredTags.size());i++){
|
||||
SearchResult res=new SearchResult(featuredTags.get(i));
|
||||
res.firstInSection=(i==0);
|
||||
results.add(res);
|
||||
}
|
||||
onDataLoaded(results, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected SearchResult getResultByID(String id){
|
||||
for(SearchResult s:data){
|
||||
if(s.id.equals(id)){
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawDivider(View child, View bottomSibling, RecyclerView.ViewHolder holder, RecyclerView.ViewHolder siblingHolder, RecyclerView parent, Canvas c, Paint paint){
|
||||
// no-op
|
||||
}
|
||||
|
||||
private void showAllPinnedPosts(){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("profileAccount", Parcels.wrap(profileAccount));
|
||||
Nav.go(getActivity(), PinnedPostsListFragment.class, args);
|
||||
}
|
||||
|
||||
private void showAllFeaturedHashtags(){
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
ArrayList<Parcelable> tags=featuredTags.stream().map(Parcels::wrap).collect(Collectors.toCollection(ArrayList::new));
|
||||
args.putParcelableArrayList("hashtags", tags);
|
||||
Nav.go(getActivity(), FeaturedHashtagsListFragment.class, args);
|
||||
}
|
||||
|
||||
private void showAllEndorsedAccounts(){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import android.graphics.drawable.LayerDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ImageSpan;
|
||||
@@ -111,7 +112,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
private ProgressBarButton actionButton;
|
||||
private ViewPager2 pager;
|
||||
private NestedRecyclerScrollView scrollView;
|
||||
private AccountTimelineFragment postsFragment, postsWithRepliesFragment, mediaFragment;
|
||||
private ProfileFeaturedFragment featuredFragment;
|
||||
private AccountTimelineFragment timelineFragment;
|
||||
private ProfileAboutFragment aboutFragment;
|
||||
private TabLayout tabbar;
|
||||
private SwipeRefreshLayout refreshLayout;
|
||||
@@ -216,14 +218,13 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
}
|
||||
};
|
||||
|
||||
tabViews=new FrameLayout[4];
|
||||
tabViews=new FrameLayout[3];
|
||||
for(int i=0;i<tabViews.length;i++){
|
||||
FrameLayout tabView=new FrameLayout(getActivity());
|
||||
tabView.setId(switch(i){
|
||||
case 0 -> R.id.profile_posts;
|
||||
case 1 -> R.id.profile_posts_with_replies;
|
||||
case 2 -> R.id.profile_media;
|
||||
case 3 -> R.id.profile_about;
|
||||
case 0 -> R.id.profile_featured;
|
||||
case 1 -> R.id.profile_timeline;
|
||||
case 2 -> R.id.profile_about;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+i);
|
||||
});
|
||||
tabView.setVisibility(View.GONE);
|
||||
@@ -245,10 +246,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
@Override
|
||||
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
||||
tab.setText(switch(position){
|
||||
case 0 -> R.string.posts;
|
||||
case 1 -> R.string.posts_and_replies;
|
||||
case 2 -> R.string.media;
|
||||
case 3 -> R.string.profile_about;
|
||||
case 0 -> R.string.profile_featured;
|
||||
case 1 -> R.string.profile_timeline;
|
||||
case 2 -> R.string.profile_about;
|
||||
default -> throw new IllegalStateException();
|
||||
});
|
||||
}
|
||||
@@ -312,12 +312,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
if(refreshing){
|
||||
refreshing=false;
|
||||
refreshLayout.setRefreshing(false);
|
||||
if(postsFragment.loaded)
|
||||
postsFragment.onRefresh();
|
||||
if(postsWithRepliesFragment.loaded)
|
||||
postsWithRepliesFragment.onRefresh();
|
||||
if(mediaFragment.loaded)
|
||||
mediaFragment.onRefresh();
|
||||
if(timelineFragment.loaded)
|
||||
timelineFragment.onRefresh();
|
||||
if(featuredFragment.loaded)
|
||||
featuredFragment.onRefresh();
|
||||
}
|
||||
V.setVisibilityAnimated(fab, View.VISIBLE);
|
||||
}
|
||||
@@ -337,10 +335,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
public void dataLoaded(){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
if(postsFragment==null){
|
||||
postsFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.DEFAULT, true);
|
||||
postsWithRepliesFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.INCLUDE_REPLIES, false);
|
||||
mediaFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.MEDIA, false);
|
||||
if(featuredFragment==null){
|
||||
featuredFragment=new ProfileFeaturedFragment();
|
||||
Bundle args=new Bundle();
|
||||
args.putString("account", accountID);
|
||||
args.putParcelable("profileAccount", Parcels.wrap(account));
|
||||
args.putBoolean("__is_tab", true);
|
||||
featuredFragment.setArguments(args);
|
||||
timelineFragment=AccountTimelineFragment.newInstance(accountID, account, GetAccountStatuses.Filter.DEFAULT, true);
|
||||
aboutFragment=new ProfileAboutFragment();
|
||||
aboutFragment.setFields(fields);
|
||||
}
|
||||
@@ -397,11 +399,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView(){
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig){
|
||||
super.onConfigurationChanged(newConfig);
|
||||
@@ -425,10 +422,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
}
|
||||
|
||||
private void applyChildWindowInsets(){
|
||||
if(postsFragment!=null && postsFragment.isAdded() && childInsets!=null){
|
||||
postsFragment.onApplyWindowInsets(childInsets);
|
||||
postsWithRepliesFragment.onApplyWindowInsets(childInsets);
|
||||
mediaFragment.onApplyWindowInsets(childInsets);
|
||||
if(timelineFragment!=null && timelineFragment.isAdded() && childInsets!=null){
|
||||
timelineFragment.onApplyWindowInsets(childInsets);
|
||||
featuredFragment.onApplyWindowInsets(childInsets);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,10 +689,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
|
||||
private Fragment getFragmentForPage(int page){
|
||||
return switch(page){
|
||||
case 0 -> postsFragment;
|
||||
case 1 -> postsWithRepliesFragment;
|
||||
case 2 -> mediaFragment;
|
||||
case 3 -> aboutFragment;
|
||||
case 0 -> featuredFragment;
|
||||
case 1 -> timelineFragment;
|
||||
case 2 -> aboutFragment;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
@@ -759,7 +754,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
invalidateOptionsMenu();
|
||||
pager.setUserInputEnabled(false);
|
||||
actionButton.setText(R.string.save_changes);
|
||||
pager.setCurrentItem(3);
|
||||
pager.setCurrentItem(2);
|
||||
for(int i=0;i<3;i++){
|
||||
tabbar.getTabAt(i).view.setEnabled(false);
|
||||
}
|
||||
@@ -1001,7 +996,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return loaded ? 4 : 0;
|
||||
return loaded ? 3 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user