Improve follow recommendations screen (AND-101)
This commit is contained in:
parent
e797d8a1c2
commit
a2ea8e76fb
|
@ -38,6 +38,7 @@ public abstract class BaseAccountListFragment extends MastodonRecyclerFragment<A
|
|||
protected HashMap<String, Relationship> relationships=new HashMap<>();
|
||||
protected String accountID;
|
||||
protected ArrayList<APIRequest<?>> relationshipsRequests=new ArrayList<>();
|
||||
protected int itemLayoutRes=R.layout.item_account_list;
|
||||
|
||||
public BaseAccountListFragment(){
|
||||
super(40);
|
||||
|
@ -151,7 +152,7 @@ public abstract class BaseAccountListFragment extends MastodonRecyclerFragment<A
|
|||
@NonNull
|
||||
@Override
|
||||
public AccountViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
AccountViewHolder holder=new AccountViewHolder(BaseAccountListFragment.this, parent, relationships);
|
||||
AccountViewHolder holder=new AccountViewHolder(BaseAccountListFragment.this, parent, relationships, itemLayoutRes);
|
||||
onConfigureViewHolder(holder);
|
||||
return holder;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.ProgressDialog;
|
|||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
|
||||
|
@ -12,35 +13,38 @@ import org.joinmastodon.android.fragments.account_list.BaseAccountListFragment;
|
|||
import org.joinmastodon.android.model.FollowSuggestion;
|
||||
import org.joinmastodon.android.model.Relationship;
|
||||
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
||||
import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.api.SimpleCallback;
|
||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class OnboardingFollowSuggestionsFragment extends BaseAccountListFragment{
|
||||
private String accountID;
|
||||
private View buttonBar;
|
||||
private ElevationOnScrollListener onScrollListener;
|
||||
private int numRunningFollowRequests=0;
|
||||
|
||||
public OnboardingFollowSuggestionsFragment(){
|
||||
super(R.layout.fragment_onboarding_follow_suggestions, 40);
|
||||
itemLayoutRes=R.layout.item_account_list_onboarding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
setRetainInstance(true);
|
||||
setTitle(R.string.popular_on_mastodon);
|
||||
setTitle(R.string.onboarding_recommendations_title);
|
||||
accountID=getArguments().getString("account");
|
||||
loadData();
|
||||
}
|
||||
|
@ -49,7 +53,6 @@ public class OnboardingFollowSuggestionsFragment extends BaseAccountListFragment
|
|||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
buttonBar=view.findViewById(R.id.button_bar);
|
||||
list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
||||
|
||||
view.findViewById(R.id.btn_next).setOnClickListener(UiUtils.rateLimitedClickListener(this::onFollowAllClick));
|
||||
view.findViewById(R.id.btn_skip).setOnClickListener(UiUtils.rateLimitedClickListener(v->proceed()));
|
||||
|
@ -58,9 +61,7 @@ public class OnboardingFollowSuggestionsFragment extends BaseAccountListFragment
|
|||
@Override
|
||||
protected void onUpdateToolbar(){
|
||||
super.onUpdateToolbar();
|
||||
if(onScrollListener!=null){
|
||||
onScrollListener.setViews(buttonBar, getToolbar());
|
||||
}
|
||||
getToolbar().setContentInsetsRelative(V.dp(56), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,7 +70,7 @@ public class OnboardingFollowSuggestionsFragment extends BaseAccountListFragment
|
|||
.setCallback(new SimpleCallback<>(this){
|
||||
@Override
|
||||
public void onSuccess(List<FollowSuggestion> result){
|
||||
onDataLoaded(result.stream().map(fs->new AccountViewModel(fs.account, accountID)).collect(Collectors.toList()), false);
|
||||
onDataLoaded(result.stream().map(fs->new AccountViewModel(fs.account, accountID).stripLinksFromBio()).collect(Collectors.toList()), false);
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
|
@ -80,6 +81,19 @@ public class OnboardingFollowSuggestionsFragment extends BaseAccountListFragment
|
|||
super.onApplyWindowInsets(UiUtils.applyBottomInsetToFixedView(buttonBar, insets));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter<?> getAdapter(){
|
||||
TextView introText=new TextView(getActivity());
|
||||
introText.setTextAppearance(R.style.m3_body_large);
|
||||
introText.setTextColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSurface));
|
||||
introText.setPaddingRelative(V.dp(56), 0, V.dp(24), V.dp(8));
|
||||
introText.setText(R.string.onboarding_recommendations_intro);
|
||||
MergeRecyclerAdapter mergeAdapter=new MergeRecyclerAdapter();
|
||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(introText));
|
||||
mergeAdapter.addAdapter(super.getAdapter());
|
||||
return mergeAdapter;
|
||||
}
|
||||
|
||||
private void onFollowAllClick(View v){
|
||||
if(!loaded || relationships.isEmpty())
|
||||
return;
|
||||
|
@ -155,5 +169,6 @@ public class OnboardingFollowSuggestionsFragment extends BaseAccountListFragment
|
|||
protected void onConfigureViewHolder(AccountViewHolder holder){
|
||||
super.onConfigureViewHolder(holder);
|
||||
holder.setStyle(AccountViewHolder.AccessoryType.BUTTON, true);
|
||||
holder.avatar.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.joinmastodon.android.model.viewmodel;
|
||||
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
|
@ -7,6 +8,7 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
|
|||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.AccountField;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.text.LinkSpan;
|
||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -43,4 +45,13 @@ public class AccountViewModel{
|
|||
}
|
||||
this.verifiedLink=verifiedLink;
|
||||
}
|
||||
|
||||
public AccountViewModel stripLinksFromBio(){
|
||||
if(parsedBio instanceof Spannable spannable){
|
||||
for(LinkSpan span:spannable.getSpans(0, spannable.length(), LinkSpan.class)){
|
||||
spannable.removeSpan(span);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import java.util.Objects;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
|
@ -53,7 +54,7 @@ import me.grishka.appkit.views.UsableRecyclerView;
|
|||
|
||||
public class AccountViewHolder extends BindableViewHolder<AccountViewModel> implements ImageLoaderViewHolder, UsableRecyclerView.Clickable, UsableRecyclerView.LongClickable{
|
||||
private final TextView name, username, followers, verifiedLink, bio;
|
||||
private final ImageView avatar;
|
||||
public final ImageView avatar;
|
||||
private final ProgressBarButton button;
|
||||
private final PopupMenu contextMenu;
|
||||
private final View menuAnchor;
|
||||
|
@ -75,7 +76,11 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
|||
private boolean checked;
|
||||
|
||||
public AccountViewHolder(Fragment fragment, ViewGroup list, HashMap<String, Relationship> relationships){
|
||||
super(fragment.getActivity(), R.layout.item_account_list, list);
|
||||
this(fragment, list, relationships, R.layout.item_account_list);
|
||||
}
|
||||
|
||||
public AccountViewHolder(Fragment fragment, ViewGroup list, HashMap<String, Relationship> relationships, @LayoutRes int layout){
|
||||
super(fragment.getActivity(), layout, list);
|
||||
this.fragment=fragment;
|
||||
this.accountID=Objects.requireNonNull(fragment.getArguments().getString("account"));
|
||||
this.relationships=relationships;
|
||||
|
@ -111,6 +116,7 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
|||
public void onBind(AccountViewModel item){
|
||||
name.setText(item.parsedName);
|
||||
username.setText("@"+item.account.acct);
|
||||
if(followers!=null){
|
||||
String followersStr=fragment.getResources().getQuantityString(R.plurals.x_followers, item.account.followersCount>1000 ? 999 : (int)item.account.followersCount);
|
||||
String followersNum=UiUtils.abbreviateNumber(item.account.followersCount);
|
||||
int index=followersStr.indexOf("%,d");
|
||||
|
@ -120,6 +126,8 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
|||
followersFormatted.setSpan(mediumSpan, index, index+followersNum.length(), 0);
|
||||
}
|
||||
followers.setText(followersFormatted);
|
||||
}
|
||||
if(verifiedLink!=null){
|
||||
boolean hasVerifiedLink=item.verifiedLink!=null;
|
||||
if(!hasVerifiedLink)
|
||||
verifiedLink.setText(R.string.no_verified_link);
|
||||
|
@ -129,6 +137,7 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
|||
int tintColor=UiUtils.getThemeColor(fragment.getActivity(), hasVerifiedLink ? R.attr.colorM3Primary : R.attr.colorM3Secondary);
|
||||
verifiedLink.setTextColor(tintColor);
|
||||
verifiedLink.setCompoundDrawableTintList(ColorStateList.valueOf(tintColor));
|
||||
}
|
||||
bindRelationship();
|
||||
if(showBio){
|
||||
bio.setText(item.parsedBio);
|
||||
|
@ -338,7 +347,7 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
|||
Menu menu=contextMenu.getMenu();
|
||||
Account account=item.account;
|
||||
|
||||
menu.findItem(R.id.share).setTitle(fragment.getString(R.string.share_user, account.getDisplayUsername()));
|
||||
menu.findItem(R.id.share).setTitle(R.string.share_user);
|
||||
menu.findItem(R.id.mute).setTitle(fragment.getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
|
||||
menu.findItem(R.id.block).setTitle(fragment.getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
|
||||
menu.findItem(R.id.report).setTitle(fragment.getString(R.string.report_user, account.getDisplayUsername()));
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple android:color="@color/m3_primary_overlay" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:tint="@color/m3_primary_alpha5" android:tintMode="src_over">
|
||||
<solid android:color="?colorM3Surface"/>
|
||||
<corners android:radius="20dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<stroke android:width="1dp" android:color="?colorM3OutlineVariant"/>
|
||||
<corners android:radius="7dp"/>
|
||||
</shape>
|
|
@ -37,16 +37,16 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:background="@drawable/bg_onboarding_panel">
|
||||
android:background="@drawable/bg_m3_surface1">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_next"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Tonal"
|
||||
style="@style/Widget.Mastodon.M3.Button.Elevated"
|
||||
android:text="@string/follow_all"/>
|
||||
|
||||
<Button
|
||||
|
@ -55,10 +55,10 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||
android:text="@string/skip"/>
|
||||
android:text="@string/next"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.joinmastodon.android.ui.views.CheckableRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/avatar"
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:foreground="@drawable/fg_onboarding_ava"
|
||||
android:padding="1dp"
|
||||
tools:src="#0f0"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:layout_toEndOf="@id/avatar"
|
||||
android:layout_toStartOf="@id/accessory"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:textAppearance="@style/m3_title_small"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
tools:text="User"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:layout_below="@id/name"
|
||||
android:layout_toEndOf="@id/avatar"
|
||||
android:layout_toStartOf="@id/accessory"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical|start"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:textColor="?colorM3Secondary"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="\@user@server"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/accessory"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="38dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginStart="8dp"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:minWidth="96dp"
|
||||
android:maxWidth="150dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||
tools:text="Follow back"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/action_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
style="?android:progressBarStyleSmall"
|
||||
android:elevation="10dp"
|
||||
android:outlineProvider="none"
|
||||
android:indeterminateTint="?colorM3OnPrimary"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:duplicateParentState="true"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/options_btn"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_gravity="top"
|
||||
android:background="?android:actionBarItemBackground"
|
||||
android:tint="?colorM3OnSurfaceVariant"
|
||||
android:contentDescription="@string/more_options"
|
||||
android:src="@drawable/ic_more_vert_24px"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bio"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/avatar"
|
||||
android:layout_below="@id/username"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
android:maxLines="2"
|
||||
android:paddingVertical="2dp"
|
||||
tools:text="bla bla bla bla bla bla"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/menu_anchor"
|
||||
android:layout_width="1px"
|
||||
android:layout_height="1px"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginLeft="-16dp"/>
|
||||
|
||||
</org.joinmastodon.android.ui.views.CheckableRelativeLayout>
|
|
@ -350,7 +350,6 @@
|
|||
<string name="profile_add_row">Add row</string>
|
||||
<string name="profile_setup">Profile setup</string>
|
||||
<string name="profile_setup_subtitle">You can always complete this later in the Profile tab.</string>
|
||||
<string name="popular_on_mastodon">Popular on Mastodon</string>
|
||||
<string name="follow_all">Follow all</string>
|
||||
<string name="server_rules_disagree">Disagree</string>
|
||||
<string name="privacy_policy_explanation">TL;DR: We don’t collect or process anything.</string>
|
||||
|
@ -664,4 +663,6 @@
|
|||
<string name="discoverability">Discoverability</string>
|
||||
<string name="discoverability_help">When you opt into discoverability on Mastodon, your posts may appear in search results and trending.\n\nYour profile may be suggested to people with similar interests to you.\n\nOpting out does not hide your profile if someone searches for you by name.</string>
|
||||
<string name="app_version_copied">Version number copied to clipboard</string>
|
||||
<string name="onboarding_recommendations_intro">You curate your own home feed.
The more people you follow, the more active and interesting it will be.</string>
|
||||
<string name="onboarding_recommendations_title">Personalize your home feed</string>
|
||||
</resources>
|
|
@ -289,6 +289,14 @@
|
|||
<item name="android:textColor">@color/button_text_m3_tonal_error</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Mastodon.M3.Button.Elevated">
|
||||
<item name="android:background">@drawable/bg_button_m3_elevated</item>
|
||||
<item name="android:textColor">@color/button_text_m3_text</item>
|
||||
<item name="android:paddingLeft">24dp</item>
|
||||
<item name="android:paddingRight">24dp</item>
|
||||
<item name="android:elevation">1dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Mastodon.M3.Button.Outlined">
|
||||
<item name="android:background">@drawable/bg_button_m3_outlined</item>
|
||||
<item name="android:textColor">@color/button_text_m3_text</item>
|
||||
|
|
Loading…
Reference in New Issue