Fixes #3389 - Show User profiles (#4678)

* Fixes #3389 - Show  User profiles

* Don't show dummy achievements data - show data only when loaded
This commit is contained in:
Ashish 2021-10-26 02:10:08 +05:30 committed by GitHub
parent 7ce80aa804
commit 88b21a678e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 358 additions and 48 deletions

View file

@ -21,6 +21,8 @@ class ContributionBoundaryCallback @Inject constructor(
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler @param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler
) : BoundaryCallback<Contribution>() { ) : BoundaryCallback<Contribution>() {
private val compositeDisposable: CompositeDisposable = CompositeDisposable() private val compositeDisposable: CompositeDisposable = CompositeDisposable()
lateinit var userName: String
/** /**
* It is triggered when the list has no items User's Contributions are then fetched from the * It is triggered when the list has no items User's Contributions are then fetched from the
@ -55,7 +57,7 @@ class ContributionBoundaryCallback @Inject constructor(
fun fetchContributions() { fun fetchContributions() {
if (sessionManager.userName != null) { if (sessionManager.userName != null) {
compositeDisposable.add( compositeDisposable.add(
mediaClient.getMediaListForUser(sessionManager.userName!!) mediaClient.getMediaListForUser(userName!!)
.map { mediaList -> .map { mediaList ->
mediaList.map { mediaList.map {
Contribution(media = it, state = Contribution.STATE_COMPLETED) Contribution(media = it, state = Contribution.STATE_COMPLETED)
@ -88,4 +90,11 @@ class ContributionBoundaryCallback @Inject constructor(
} }
) )
} }
/**
* Clean up
*/
fun dispose() {
compositeDisposable.dispose()
}
} }

View file

@ -5,7 +5,6 @@ import static android.view.View.VISIBLE;
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE; import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -21,6 +20,7 @@ import android.widget.ProgressBar;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -35,17 +35,20 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.DialogUtil;
import fr.free.nrw.commons.media.MediaClient; import fr.free.nrw.commons.media.MediaClient;
import fr.free.nrw.commons.utils.SystemThemeUtils; import fr.free.nrw.commons.utils.SystemThemeUtils;
import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.utils.ViewUtil;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.WikiSite;
import timber.log.Timber; import fr.free.nrw.commons.profile.ProfileActivity;
/** /**
* Created by root on 01.06.2018. * Created by root on 01.06.2018.
@ -76,6 +79,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
@Inject @Inject
SystemThemeUtils systemThemeUtils; SystemThemeUtils systemThemeUtils;
@BindView(R.id.tv_contributions_of_user)
AppCompatTextView tvContributionsOfUser;
@Inject @Inject
ContributionController controller; ContributionController controller;
@ -89,6 +94,9 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
@Inject @Inject
ContributionsListPresenter contributionsListPresenter; ContributionsListPresenter contributionsListPresenter;
@Inject
SessionManager sessionManager;
private Animation fab_close; private Animation fab_close;
private Animation fab_open; private Animation fab_open;
private Animation rotate_forward; private Animation rotate_forward;
@ -105,8 +113,23 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
private final int SPAN_COUNT_PORTRAIT = 1; private final int SPAN_COUNT_PORTRAIT = 1;
private int contributionsSize; private int contributionsSize;
String userName;
@Override
public void onCreate(@Nullable @org.jetbrains.annotations.Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Now that we are allowing this fragment to be started for
// any userName- we expect it to be passed as an argument
if (getArguments() != null) {
userName = getArguments().getString(ProfileActivity.KEY_USERNAME);
}
if (StringUtils.isEmpty(userName)) {
userName = sessionManager.getUserName();
}
}
@Override @Override
public View onCreateView( public View onCreateView(
final LayoutInflater inflater, @Nullable final ViewGroup container, final LayoutInflater inflater, @Nullable final ViewGroup container,
@ -114,6 +137,16 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
final View view = inflater.inflate(R.layout.fragment_contributions_list, container, false); final View view = inflater.inflate(R.layout.fragment_contributions_list, container, false);
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
contributionsListPresenter.onAttachView(this); contributionsListPresenter.onAttachView(this);
if (Objects.equals(sessionManager.getUserName(), userName)) {
tvContributionsOfUser.setVisibility(GONE);
fab_layout.setVisibility(VISIBLE);
} else {
tvContributionsOfUser.setVisibility(VISIBLE);
tvContributionsOfUser.setText(getString(R.string.contributions_of_user, userName));
fab_layout.setVisibility(GONE);
}
initAdapter(); initAdapter();
return view; return view;
} }
@ -155,8 +188,9 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false); ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
} }
contributionsListPresenter.setup(); contributionsListPresenter.setup(userName,
contributionsListPresenter.contributionList.observe(this.getViewLifecycleOwner(), list -> { Objects.equals(sessionManager.getUserName(), userName));
contributionsListPresenter.contributionList.observe(getViewLifecycleOwner(), list -> {
contributionsSize = list.size(); contributionsSize = list.size();
adapter.submitList(list); adapter.submitList(list);
if (callback != null) { if (callback != null) {

View file

@ -1,6 +1,9 @@
package fr.free.nrw.commons.contributions; package fr.free.nrw.commons.contributions;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.paging.DataSource;
import androidx.paging.DataSource.Factory;
import androidx.paging.LivePagedListBuilder; import androidx.paging.LivePagedListBuilder;
import androidx.paging.PagedList; import androidx.paging.PagedList;
import fr.free.nrw.commons.contributions.ContributionsListContract.UserActionListener; import fr.free.nrw.commons.contributions.ContributionsListContract.UserActionListener;
@ -20,17 +23,20 @@ public class ContributionsListPresenter implements UserActionListener {
private final Scheduler ioThreadScheduler; private final Scheduler ioThreadScheduler;
private final CompositeDisposable compositeDisposable; private final CompositeDisposable compositeDisposable;
private final ContributionsRemoteDataSource contributionsRemoteDataSource;
LiveData<PagedList<Contribution>> contributionList; LiveData<PagedList<Contribution>> contributionList;
@Inject @Inject
ContributionsListPresenter( ContributionsListPresenter(
final ContributionBoundaryCallback contributionBoundaryCallback, final ContributionBoundaryCallback contributionBoundaryCallback,
final ContributionsRemoteDataSource contributionsRemoteDataSource,
final ContributionsRepository repository, final ContributionsRepository repository,
@Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) { @Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) {
this.contributionBoundaryCallback = contributionBoundaryCallback; this.contributionBoundaryCallback = contributionBoundaryCallback;
this.repository = repository; this.repository = repository;
this.ioThreadScheduler = ioThreadScheduler; this.ioThreadScheduler = ioThreadScheduler;
this.contributionsRemoteDataSource=contributionsRemoteDataSource;
compositeDisposable = new CompositeDisposable(); compositeDisposable = new CompositeDisposable();
} }
@ -43,19 +49,44 @@ public class ContributionsListPresenter implements UserActionListener {
* the live data object. This method can be tweaked to update the lazy loading behavior of the * the live data object. This method can be tweaked to update the lazy loading behavior of the
* contributions list * contributions list
*/ */
void setup() { void setup(String userName, boolean isSelf) {
final PagedList.Config pagedListConfig = final PagedList.Config pagedListConfig =
(new PagedList.Config.Builder()) (new PagedList.Config.Builder())
.setPrefetchDistance(50) .setPrefetchDistance(50)
.setPageSize(10).build(); .setPageSize(10).build();
contributionList = (new LivePagedListBuilder(repository.fetchContributions(), Factory<Integer, Contribution> factory;
pagedListConfig) boolean shouldSetBoundaryCallback;
.setBoundaryCallback(contributionBoundaryCallback)).build(); if (!isSelf) {
//We don't want to persist contributions for other user's, therefore
// creating a new DataSource for them
contributionsRemoteDataSource.setUserName(userName);
factory = new Factory<Integer, Contribution>() {
@NonNull
@Override
public DataSource<Integer, Contribution> create() {
return contributionsRemoteDataSource;
}
};
shouldSetBoundaryCallback = false;
} else {
contributionBoundaryCallback.setUserName(userName);
shouldSetBoundaryCallback = true;
factory = repository.fetchContributions();
}
LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, pagedListConfig);
if (shouldSetBoundaryCallback) {
livePagedListBuilder.setBoundaryCallback(contributionBoundaryCallback);
}
contributionList = livePagedListBuilder.build();
} }
@Override @Override
public void onDetachView() { public void onDetachView() {
compositeDisposable.clear(); compositeDisposable.clear();
contributionsRemoteDataSource.dispose();
contributionBoundaryCallback.dispose();
} }
/** /**

View file

@ -0,0 +1,73 @@
package fr.free.nrw.commons.contributions
import androidx.paging.ItemKeyedDataSource
import fr.free.nrw.commons.di.CommonsApplicationModule
import fr.free.nrw.commons.media.MediaClient
import io.reactivex.Scheduler
import io.reactivex.disposables.CompositeDisposable
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
/**
* Data-Source which acts as mediator for contributions-data from the API
*/
class ContributionsRemoteDataSource @Inject constructor(
private val mediaClient: MediaClient,
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler
) : ItemKeyedDataSource<Int, Contribution>() {
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
var userName: String? = null
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Contribution>
) {
fetchContributions(callback)
}
override fun loadAfter(
params: LoadParams<Int>,
callback: LoadCallback<Contribution>
) {
fetchContributions(callback)
}
override fun loadBefore(
params: LoadParams<Int>,
callback: LoadCallback<Contribution>
) {
}
override fun getKey(item: Contribution): Int {
return item.pageId.hashCode()
}
/**
* Fetches contributions using the MediaWiki API
*/
private fun fetchContributions(callback: LoadCallback<Contribution>) {
compositeDisposable.add(
mediaClient.getMediaListForUser(userName!!)
.map { mediaList ->
mediaList.map {
Contribution(media = it, state = Contribution.STATE_COMPLETED)
}
}
.subscribeOn(ioThreadScheduler)
.subscribe({
callback.onResult(it)
}) { error: Throwable ->
Timber.e(
"Failed to fetch contributions: %s",
error.message
)
}
)
}
fun dispose() {
compositeDisposable.dispose()
}
}

View file

@ -75,6 +75,7 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity; import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.nearby.Label; import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.profile.ProfileActivity;
import fr.free.nrw.commons.ui.widget.HtmlTextView; import fr.free.nrw.commons.ui.widget.HtmlTextView;
import fr.free.nrw.commons.utils.ViewUtilWrapper; import fr.free.nrw.commons.utils.ViewUtilWrapper;
import io.reactivex.Single; import io.reactivex.Single;
@ -1014,6 +1015,15 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
} }
} }
@OnClick(R.id.mediaDetailAuthor)
public void onAuthorViewClicked() {
if (media == null || media.getUser() == null) {
return;
}
ProfileActivity.startYourself(getActivity(), media.getUser(), !Objects
.equals(sessionManager.getUserName(), media.getUser()));
}
/** /**
* Enable Progress Bar and Update delete button text. * Enable Progress Bar and Update delete button text.
*/ */

View file

@ -32,6 +32,7 @@ import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.profile.ProfileActivity;
import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.theme.BaseActivity;
import fr.free.nrw.commons.utils.DownloadUtils; import fr.free.nrw.commons.utils.DownloadUtils;
import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.utils.ImageUtils;
@ -201,6 +202,11 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
// Set avatar // Set avatar
setAvatar(m); setAvatar(m);
return true; return true;
case R.id.menu_view_user_page:
if (m != null && m.getUser() != null) {
ProfileActivity.startYourself(getActivity(), m.getUser(),
!Objects.equals(sessionManager.getUserName(), m.getUser()));
}
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@ -258,7 +264,9 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
menu.findItem(R.id.menu_download_current_image).setEnabled(true).setVisible(true); menu.findItem(R.id.menu_download_current_image).setEnabled(true).setVisible(true);
menu.findItem(R.id.menu_bookmark_current_image).setEnabled(true).setVisible(true); menu.findItem(R.id.menu_bookmark_current_image).setEnabled(true).setVisible(true);
menu.findItem(R.id.menu_set_as_wallpaper).setEnabled(true).setVisible(true); menu.findItem(R.id.menu_set_as_wallpaper).setEnabled(true).setVisible(true);
if (m.getUser() != null) {
menu.findItem(R.id.menu_view_user_page).setEnabled(true).setVisible(true);
}
// Initialize bookmark object // Initialize bookmark object
bookmark = new Bookmark( bookmark = new Bookmark(
m.getFilename(), m.getFilename(),

View file

@ -74,13 +74,20 @@ public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
* Set the username in navigationHeader. * Set the username in navigationHeader.
*/ */
private void setUserName() { private void setUserName() {
AccountManager accountManager = AccountManager.get(getActivity()); moreProfile.setText(getUserName());
Account[] allAccounts = accountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE); }
private String getUserName(){
final AccountManager accountManager = AccountManager.get(getActivity());
final Account[] allAccounts = accountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE);
if (allAccounts.length != 0) { if (allAccounts.length != 0) {
moreProfile.setText(allAccounts[0].name); moreProfile.setText(allAccounts[0].name);
return allAccounts[0].name;
} }
return "";
} }
@OnClick(R.id.more_logout) @OnClick(R.id.more_logout)
public void onLogoutClicked() { public void onLogoutClicked() {
new AlertDialog.Builder(getActivity()) new AlertDialog.Builder(getActivity())
@ -136,9 +143,7 @@ public class MoreBottomSheetFragment extends BottomSheetDialogFragment {
@OnClick(R.id.more_profile) @OnClick(R.id.more_profile)
public void onProfileClicked() { public void onProfileClicked() {
final Intent intent = new Intent(getActivity(), ProfileActivity.class); ProfileActivity.startYourself(getActivity(), getUserName(), false);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
getActivity().startActivity(intent);
} }
@OnClick(R.id.more_peer_review) @OnClick(R.id.more_peer_review)

View file

@ -13,6 +13,7 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@ -25,6 +26,7 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.ViewPagerAdapter; import fr.free.nrw.commons.ViewPagerAdapter;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.ContributionsListFragment;
import fr.free.nrw.commons.profile.achievements.AchievementsFragment; import fr.free.nrw.commons.profile.achievements.AchievementsFragment;
import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment; import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment;
import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.theme.BaseActivity;
@ -49,9 +51,6 @@ public class ProfileActivity extends BaseActivity {
@BindView(R.id.tab_layout) @BindView(R.id.tab_layout)
TabLayout tabLayout; TabLayout tabLayout;
@BindView(R.id.toolbar)
Toolbar toolbar;
@Inject @Inject
SessionManager sessionManager; SessionManager sessionManager;
@ -59,15 +58,32 @@ public class ProfileActivity extends BaseActivity {
private AchievementsFragment achievementsFragment; private AchievementsFragment achievementsFragment;
private LeaderboardFragment leaderboardFragment; private LeaderboardFragment leaderboardFragment;
public static final String KEY_USERNAME ="username";
public static final String KEY_SHOULD_SHOW_CONTRIBUTIONS ="shouldShowContributions";
String userName;
private boolean shouldShowContributions;
@Override
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
userName = savedInstanceState.getString(KEY_USERNAME);
shouldShowContributions = savedInstanceState.getBoolean(KEY_SHOULD_SHOW_CONTRIBUTIONS);
}
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_profile); setContentView(R.layout.activity_profile);
ButterKnife.bind(this); ButterKnife.bind(this);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setTitle(sessionManager.getUserName()); setTitle(sessionManager.getUserName());
userName = getIntent().getStringExtra(KEY_USERNAME);
shouldShowContributions = getIntent().getBooleanExtra(KEY_SHOULD_SHOW_CONTRIBUTIONS, false);
supportFragmentManager = getSupportFragmentManager(); supportFragmentManager = getSupportFragmentManager();
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(viewPagerAdapter); viewPager.setAdapter(viewPagerAdapter);
@ -87,11 +103,15 @@ public class ProfileActivity extends BaseActivity {
/** /**
* Creates a way to change current activity to AchievementActivity * Creates a way to change current activity to AchievementActivity
*
* @param context * @param context
*/ */
public static void startYourself(Context context) { public static void startYourself(final Context context, final String userName,
final boolean shouldShowContributions) {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(KEY_USERNAME, userName);
intent.putExtra(KEY_SHOULD_SHOW_CONTRIBUTIONS, shouldShowContributions);
context.startActivity(intent); context.startActivity(intent);
} }
@ -102,11 +122,28 @@ public class ProfileActivity extends BaseActivity {
List<Fragment> fragmentList = new ArrayList<>(); List<Fragment> fragmentList = new ArrayList<>();
List<String> titleList = new ArrayList<>(); List<String> titleList = new ArrayList<>();
achievementsFragment = new AchievementsFragment(); achievementsFragment = new AchievementsFragment();
Bundle achievementsBundle = new Bundle();
achievementsBundle.putString(KEY_USERNAME, userName);
achievementsFragment.setArguments(achievementsBundle);
fragmentList.add(achievementsFragment); fragmentList.add(achievementsFragment);
titleList.add(getResources().getString(R.string.achievements_tab_title).toUpperCase()); titleList.add(getResources().getString(R.string.achievements_tab_title).toUpperCase());
leaderboardFragment = new LeaderboardFragment(); leaderboardFragment = new LeaderboardFragment();
Bundle leaderBoardBundle = new Bundle();
leaderBoardBundle.putString(KEY_USERNAME, userName);
leaderboardFragment.setArguments(leaderBoardBundle);
fragmentList.add(leaderboardFragment); fragmentList.add(leaderboardFragment);
titleList.add(getResources().getString(R.string.leaderboard_tab_title).toUpperCase()); titleList.add(getResources().getString(R.string.leaderboard_tab_title).toUpperCase());
if (shouldShowContributions) {
ContributionsListFragment contributionsListFragment = new ContributionsListFragment();
Bundle contributionsListBundle = new Bundle();
contributionsListBundle.putString(KEY_USERNAME, userName);
contributionsListFragment.setArguments(contributionsListBundle);
fragmentList.add(contributionsListFragment);
titleList.add(getString(R.string.contributions_fragment).toUpperCase());
}
viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.setTabData(fragmentList, titleList);
viewPagerAdapter.notifyDataSetChanged(); viewPagerAdapter.notifyDataSetChanged();
@ -191,4 +228,10 @@ public class ProfileActivity extends BaseActivity {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override
protected void onSaveInstanceState(@NonNull final Bundle outState) {
outState.putString(KEY_USERNAME, userName);
super.onSaveInstanceState(outState);
}
} }

View file

@ -15,7 +15,9 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.view.ContextThemeWrapper; import androidx.appcompat.view.ContextThemeWrapper;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import butterknife.BindView; import butterknife.BindView;
@ -28,6 +30,7 @@ import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.utils.ViewUtil;
import fr.free.nrw.commons.profile.ProfileActivity;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
@ -114,6 +117,9 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
@BindView(R.id.wikidata_edits) @BindView(R.id.wikidata_edits)
TextView wikidataEditsText; TextView wikidataEditsText;
@BindView(R.id.tv_achievements_of_user)
AppCompatTextView tvAchievementsOfUser;
@Inject @Inject
SessionManager sessionManager; SessionManager sessionManager;
@ -128,6 +134,16 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
// menu item for action bar // menu item for action bar
private MenuItem item; private MenuItem item;
private String userName;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
userName = getArguments().getString(ProfileActivity.KEY_USERNAME);
}
}
/** /**
* This method helps in the creation Achievement screen and * This method helps in the creation Achievement screen and
* dynamically set the size of imageView * dynamically set the size of imageView
@ -157,6 +173,12 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
// Set the initial value of WikiData edits to 0 // Set the initial value of WikiData edits to 0
wikidataEditsText.setText("0"); wikidataEditsText.setText("0");
if(sessionManager.getUserName().equals(userName)){
tvAchievementsOfUser.setVisibility(View.GONE);
}else{
tvAchievementsOfUser.setVisibility(View.VISIBLE);
tvAchievementsOfUser.setText(getString(R.string.achievements_of_user,userName));
}
setWikidataEditCount(); setWikidataEditCount();
setAchievements(); setAchievements();
return rootView; return rootView;
@ -182,7 +204,7 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
try{ try{
compositeDisposable.add(okHttpJsonApiClient compositeDisposable.add(okHttpJsonApiClient
.getAchievements(Objects.requireNonNull(sessionManager.getCurrentAccount()).name) .getAchievements(Objects.requireNonNull(userName))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
@ -225,7 +247,6 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
* in the form of JavaRx Single object<JSONobject> * in the form of JavaRx Single object<JSONobject>
*/ */
private void setWikidataEditCount() { private void setWikidataEditCount() {
String userName = sessionManager.getUserName();
if (StringUtils.isBlank(userName)) { if (StringUtils.isBlank(userName)) {
return; return;
} }
@ -274,7 +295,7 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
private void setUploadCount(Achievements achievements) { private void setUploadCount(Achievements achievements) {
if (checkAccount()) { if (checkAccount()) {
compositeDisposable.add(okHttpJsonApiClient compositeDisposable.add(okHttpJsonApiClient
.getUploadCount(Objects.requireNonNull(sessionManager.getCurrentAccount()).name) .getUploadCount(Objects.requireNonNull(userName))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
@ -304,7 +325,7 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
if (uploadCount==0){ if (uploadCount==0){
setZeroAchievements(); setZeroAchievements();
}else { }else {
imagesUploadedProgressbar.setVisibility(View.VISIBLE);
imagesUploadedProgressbar.setProgress imagesUploadedProgressbar.setProgress
(100*uploadCount/levelInfo.getMaxUploadCount()); (100*uploadCount/levelInfo.getMaxUploadCount());
imagesUploadedProgressbar.setProgressTextFormatPattern imagesUploadedProgressbar.setProgressTextFormatPattern
@ -314,8 +335,12 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
} }
private void setZeroAchievements() { private void setZeroAchievements() {
AlertDialog.Builder builder=new AlertDialog.Builder(getActivity()) AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.no_achievements_yet)) .setMessage(
!Objects.equals(sessionManager.getUserName(), userName) ?
getString(R.string.no_achievements_yet, userName) :
getString(R.string.you_have_no_achievements_yet)
)
.setPositiveButton(getString(R.string.ok), (dialog, which) -> { .setPositiveButton(getString(R.string.ok), (dialog, which) -> {
}); });
AlertDialog dialog = builder.create(); AlertDialog dialog = builder.create();
@ -336,6 +361,7 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
* @param notRevertPercentage * @param notRevertPercentage
*/ */
private void setImageRevertPercentage(int notRevertPercentage){ private void setImageRevertPercentage(int notRevertPercentage){
imageRevertsProgressbar.setVisibility(View.VISIBLE);
imageRevertsProgressbar.setProgress(notRevertPercentage); imageRevertsProgressbar.setProgress(notRevertPercentage);
String revertPercentage = Integer.toString(notRevertPercentage); String revertPercentage = Integer.toString(notRevertPercentage);
imageRevertsProgressbar.setProgressTextFormatPattern(revertPercentage + "%%"); imageRevertsProgressbar.setProgressTextFormatPattern(revertPercentage + "%%");
@ -348,6 +374,7 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
* @param achievements * @param achievements
*/ */
private void inflateAchievements(Achievements achievements) { private void inflateAchievements(Achievements achievements) {
imagesUsedByWikiProgressBar.setVisibility(View.VISIBLE);
thanksReceived.setText(String.valueOf(achievements.getThanksReceived())); thanksReceived.setText(String.valueOf(achievements.getThanksReceived()));
imagesUsedByWikiProgressBar.setProgress imagesUsedByWikiProgressBar.setProgress
(100 * achievements.getUniqueUsedImages() / levelInfo.getMaxUniqueImages()); (100 * achievements.getUniqueUsedImages() / levelInfo.getMaxUniqueImages());
@ -481,5 +508,4 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
} }
return true; return true;
} }
} }

View file

@ -17,6 +17,7 @@ import android.widget.Button;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.MergeAdapter; import androidx.recyclerview.widget.MergeAdapter;
@ -27,6 +28,7 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.profile.ProfileActivity;
import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
@ -104,6 +106,16 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
*/ */
private boolean scrollToRank; private boolean scrollToRank;
private String userName;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
userName = getArguments().getString(ProfileActivity.KEY_USERNAME);
}
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_leaderboard, container, false); View rootView = inflater.inflate(R.layout.fragment_leaderboard, container, false);
@ -220,7 +232,7 @@ public class LeaderboardFragment extends CommonsDaggerSupportFragment {
if (checkAccount()) { if (checkAccount()) {
try { try {
compositeDisposable.add(okHttpJsonApiClient compositeDisposable.add(okHttpJsonApiClient
.getLeaderboard(Objects.requireNonNull(sessionManager.getCurrentAccount()).name, .getLeaderboard(Objects.requireNonNull(userName),
duration, category, null, null) duration, category, null, null)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())

View file

@ -1,7 +1,7 @@
package fr.free.nrw.commons.profile.leaderboard; package fr.free.nrw.commons.profile.leaderboard;
import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.USER_LINK_PREFIX;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -14,6 +14,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView; import com.facebook.drawee.view.SimpleDraweeView;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.profile.ProfileActivity;
/** /**
* This class extends RecyclerView.Adapter and creates the List section of the leaderboard * This class extends RecyclerView.Adapter and creates the List section of the leaderboard
@ -81,9 +82,13 @@ public class LeaderboardListAdapter extends PagedListAdapter<LeaderboardList, Le
count.setText(getItem(position).getCategoryCount().toString()); count.setText(getItem(position).getCategoryCount().toString());
/* /*
Open the user profile in a webview when a username is clicked on leaderboard Now that we have our in app profile-section, lets take the user there
*/ */
holder.itemView.setOnClickListener(view -> Utils.handleWebUrl(holder.getContext(), Uri.parse( holder.itemView.setOnClickListener(view -> {
String.format("%s%s", USER_LINK_PREFIX, getItem(position).getUsername())))); if (view.getContext() instanceof ProfileActivity) {
((Activity) (view.getContext())).finish();
}
ProfileActivity.startYourself(view.getContext(), getItem(position).getUsername(), true);
});
} }
} }

View file

@ -15,8 +15,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/mainBackground"> android:background="?attr/mainBackground">
<include layout="@layout/toolbar"/>
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout" android:id="@+id/tab_layout"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -15,6 +15,14 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_achievements_of_user"
style="@style/MediaDetailTextLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
tools:text="Achievements of user : Ashish" />
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -136,6 +144,7 @@
app:progress_start_color="#3A3381" app:progress_start_color="#3A3381"
app:progress_stroke_width="@dimen/progressbar_stroke" app:progress_stroke_width="@dimen/progressbar_stroke"
app:progress_text_format_pattern="573/110" app:progress_text_format_pattern="573/110"
android:visibility="gone"
app:progress_text_color="@color/secondaryColor" app:progress_text_color="@color/secondaryColor"
app:style="solid_line" /> app:style="solid_line" />
@ -203,6 +212,7 @@
android:layout_marginRight="@dimen/large_gap" android:layout_marginRight="@dimen/large_gap"
android:layout_marginEnd="@dimen/large_gap" android:layout_marginEnd="@dimen/large_gap"
android:progress="50" android:progress="50"
android:visibility="gone"
android:id="@+id/image_reverts_progressbar" android:id="@+id/image_reverts_progressbar"
app:progress_end_color="#8C8B98" app:progress_end_color="#8C8B98"
app:progress_start_color="#3A3381" app:progress_start_color="#3A3381"
@ -270,6 +280,7 @@
app:progress_end_color="#8C8B98" app:progress_end_color="#8C8B98"
app:progress_start_color="#3A3381" app:progress_start_color="#3A3381"
app:progress_stroke_width="2.5dp" app:progress_stroke_width="2.5dp"
android:visibility="gone"
app:progress_text_color="@color/secondaryColor" app:progress_text_color="@color/secondaryColor"
app:progress_text_format_pattern="12/24" app:progress_text_format_pattern="12/24"
app:style="solid_line" /> app:style="solid_line" />

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -26,11 +27,28 @@
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:visibility="gone" android:visibility="gone"
/> />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_contributions_of_user"
style="@style/MediaDetailTextLabel"
tools:text="Contributions of user : Ashish"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
tools:visibility="visible"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/contributionsList" android:id="@+id/contributionsList"
android:layout_height="match_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
/> android:layout_height="match_parent"
android:layout_below="@id/tv_contributions_of_user" />
</RelativeLayout>
<LinearLayout <LinearLayout
android:id="@+id/fab_layout" android:id="@+id/fab_layout"

View file

@ -30,4 +30,9 @@
android:title="@string/menu_set_avatar" android:title="@string/menu_set_avatar"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/menu_view_user_page"
android:title="@string/menu_view_user_page"
app:showAsAction="never" />
</menu> </menu>

View file

@ -527,7 +527,8 @@ Upload your first media by tapping on the add button.</string>
<string name="coordinates_picking_unsuccessful">Unable to get coordinates.</string> <string name="coordinates_picking_unsuccessful">Unable to get coordinates.</string>
<string name="share_image_via">Share image via</string> <string name="share_image_via">Share image via</string>
<string name="no_achievements_yet">You haven\'t made any contributions yet</string> <string name="you_have_no_achievements_yet">You haven\'t made any contributions yet</string>
<string name="no_achievements_yet">%s has not made any contributions yet</string>
<string name="account_created">Account created!</string> <string name="account_created">Account created!</string>
<string name="text_copy">Text copied to clipboard</string> <string name="text_copy">Text copied to clipboard</string>
<string name="notification_mark_read">Notification marked as read</string> <string name="notification_mark_read">Notification marked as read</string>
@ -656,4 +657,7 @@ Upload your first media by tapping on the add button.</string>
<string name="learn_more">LEARN MORE</string> <string name="learn_more">LEARN MORE</string>
<string name="wlm_campaign_title">Wiki Loves Monuments</string> <string name="wlm_campaign_title">Wiki Loves Monuments</string>
<string name="wlm_campaign_description">Wiki Loves Monuments is an international photo contest for monuments organised by Wikimedia</string> <string name="wlm_campaign_description">Wiki Loves Monuments is an international photo contest for monuments organised by Wikimedia</string>
<string name="contributions_of_user">Contributions of User: %s</string>
<string name="achievements_of_user">Achievements of User: %s</string>
<string name="menu_view_user_page">View user page</string>
</resources> </resources>

View file

@ -49,7 +49,8 @@ class ContributionBoundaryCallbackTest {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
scheduler = Schedulers.trampoline() scheduler = Schedulers.trampoline()
contributionBoundaryCallback = contributionBoundaryCallback =
ContributionBoundaryCallback(repository, sessionManager, mediaClient, scheduler); ContributionBoundaryCallback(repository, sessionManager, mediaClient, scheduler)
contributionBoundaryCallback.userName = "test"
} }
@Test @Test

View file

@ -32,6 +32,9 @@ class ContributionsListPresenterTest {
@Mock @Mock
internal lateinit var repository: ContributionsRepository internal lateinit var repository: ContributionsRepository
@Mock
internal lateinit var remoteDataSource: ContributionsRemoteDataSource
@Rule @Rule
@JvmField @JvmField
var instantTaskExecutorRule = InstantTaskExecutorRule() var instantTaskExecutorRule = InstantTaskExecutorRule()
@ -49,7 +52,12 @@ class ContributionsListPresenterTest {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
scheduler = Schedulers.trampoline() scheduler = Schedulers.trampoline()
contributionsListPresenter = contributionsListPresenter =
ContributionsListPresenter(contributionBoundaryCallback, repository, scheduler); ContributionsListPresenter(
contributionBoundaryCallback,
remoteDataSource,
repository,
scheduler
);
} }
@Test @Test

View file

@ -80,7 +80,7 @@ class ProfileActivityTest {
@Test @Test
@Throws(Exception::class) @Throws(Exception::class)
fun testStartYourself() { fun testStartYourself() {
ProfileActivity.startYourself(activity) ProfileActivity.startYourself(activity, "test", false)
} }
@Test @Test

View file

@ -11,12 +11,14 @@ import androidx.fragment.app.FragmentTransaction
import com.dinuscxj.progressbar.CircleProgressBar import com.dinuscxj.progressbar.CircleProgressBar
import fr.free.nrw.commons.TestAppAdapter import fr.free.nrw.commons.TestAppAdapter
import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.profile.ProfileActivity
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.powermock.reflect.Whitebox import org.powermock.reflect.Whitebox
import org.robolectric.Robolectric import org.robolectric.Robolectric
@ -85,6 +87,9 @@ class AchievementsFragmentUnitTests {
@Mock @Mock
private lateinit var progressBar: ProgressBar private lateinit var progressBar: ProgressBar
@Mock
private lateinit var sessionManager: SessionManager
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
@ -131,6 +136,10 @@ class AchievementsFragmentUnitTests {
Whitebox.setInternalState(fragment, "progressBar", progressBar) Whitebox.setInternalState(fragment, "progressBar", progressBar)
Whitebox.setInternalState(fragment, "imagesRevertLimitText", imagesRevertLimitText) Whitebox.setInternalState(fragment, "imagesRevertLimitText", imagesRevertLimitText)
Whitebox.setInternalState(fragment, "item", menuItem) Whitebox.setInternalState(fragment, "item", menuItem)
Whitebox.setInternalState(fragment, "sessionManager", sessionManager)
Mockito.`when`(sessionManager.userName).thenReturn("Test")
} }
@Test @Test