join / leave a challenge, open profile of the challenge leader, multiple fixes

This commit is contained in:
Negue 2016-12-13 21:04:15 +01:00
parent f4aa65b6b4
commit a1f9aab852
11 changed files with 279 additions and 70 deletions

View file

@ -2,10 +2,10 @@
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/card_view"
style="@style/CardView.Default"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp">
android:layout_margin="5dp"
android:clickable="true">
<LinearLayout
style="@style/CardContent.Compact"
@ -17,46 +17,86 @@
<TextView
android:id="@+id/challenge_name"
android:layout_width="match_parent"
android:textStyle="bold"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:textStyle="bold" />
<TextView
android:id="@+id/challenge_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="3"/>
android:maxLines="3" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:baselineAligned="false"
android:weightSum="1.0">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="30dp"
android:layout_weight="0.5">
<TextView
android:id="@+id/leader_label"
<LinearLayout
android:id="@+id/user_background_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|center"
android:background="@drawable/layout_rounded_bg"
android:backgroundTint="@color/days_gray">
android:ellipsize="middle"
android:lines="1" />
<TextView
android:id="@+id/user_label"
style="@style/ChatMessageUserTextViewStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:ellipsize="middle"
android:text="design_data"
android:lines="1" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="30dp"
android:layout_weight="0.5"
android:gravity="right">
android:gravity="right|fill_vertical">
<LinearLayout
android:id="@+id/gem_prize_layout"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginEnd="15dp"
android:layout_marginRight="15dp"
android:gravity="end">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:src="@drawable/ic_header_gem" />
<TextView
android:id="@+id/gemPrizeTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:ellipsize="middle"
android:lines="1"
android:paddingLeft="5dp"
android:paddingStart="5dp"
android:text="0" />
</LinearLayout>
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:src="@drawable/ic_people_black_24dp" />
<TextView
@ -77,37 +117,36 @@
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:gravity="end"
android:weightSum="1.0">
android:baselineAligned="false">
<LinearLayout
android:id="@+id/gem_prize_layout"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:gravity="end">
android:layout_weight="1">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_header_gem" />
<TextView
android:id="@+id/gemPrizeTextView"
<Button
android:id="@+id/challenge_button_leave"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:ellipsize="middle"
android:lines="1"
android:paddingLeft="5dp"
android:paddingStart="5dp"
android:text="0" />
android:layout_height="match_parent"
android:gravity="start|fill_vertical"
android:text="@string/leave"
android:textColor="@color/md_red_400" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end|fill_vertical">
<Button
android:id="@+id/challenge_button_join"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/join" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

View file

@ -438,4 +438,8 @@ To start, which parts of your life do you want to improve?</string>
<string name="AudioTheme_summary">Change Habitica\'s Audio Theme</string>
<string name="byLeader">by %s</string>
<string name="challenge_details">Challenge Details</string>
<string name="challenge_leave_title">Are you sure?</string>
<string name="challenge_leave_text">Are you sure you want to leave the Challenge "%s"?</string>
<string name="my_challenges">My Challenges</string>
<string name="public_challenges">Public</string>
</resources>

View file

@ -0,0 +1,11 @@
package com.habitrpg.android.habitica.events.commands;
public class JoinChallengeCommand {
public String challengeId;
public JoinChallengeCommand(String challengeId){
this.challengeId = challengeId;
}
}

View file

@ -0,0 +1,9 @@
package com.habitrpg.android.habitica.events.commands;
public class LeaveChallengeCommand {
public String challengeId;
public LeaveChallengeCommand(String challengeId){
this.challengeId = challengeId;
}
}

View file

@ -3,18 +3,23 @@ package com.habitrpg.android.habitica.ui.activities;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.ui.adapter.social.ChallengesListViewAdapter.ChallengeViewHolder;
import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeTasksFragment;
import com.magicmicky.habitrpgwrapper.lib.models.Challenge;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ChallengeDetailActivity extends BaseActivity {
@ -73,4 +78,51 @@ public class ChallengeDetailActivity extends BaseActivity {
protected void injectActivity(AppComponent component) {
component.inject(this);
}
public static class ChallengeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@BindView(R.id.challenge_name)
TextView challengeName;
@BindView(R.id.challenge_description)
TextView challengeDescription;
@BindView(R.id.memberCountTextView)
TextView memberCountTextView;
@BindView(R.id.gem_prize_layout)
LinearLayout gem_prize_layout;
@BindView(R.id.gemPrizeTextView)
TextView gemPrizeTextView;
private Challenge challenge;
public ChallengeViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
public void bind(Challenge challenge) {
this.challenge = challenge;
challengeName.setText(challenge.name);
challengeDescription.setText(challenge.description);
memberCountTextView.setText(challenge.memberCount + "");
if (challenge.prize == 0) {
gem_prize_layout.setVisibility(View.GONE);
} else {
gem_prize_layout.setVisibility(View.VISIBLE);
gemPrizeTextView.setText(challenge.prize + "");
}
}
@Override
public void onClick(View view) {
}
}
}

View file

@ -1,16 +1,22 @@
package com.habitrpg.android.habitica.ui.adapter.social;
import android.content.Context;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.events.commands.JoinChallengeCommand;
import com.habitrpg.android.habitica.events.commands.LeaveChallengeCommand;
import com.habitrpg.android.habitica.events.commands.OpenFullProfileCommand;
import com.habitrpg.android.habitica.events.commands.ShowChallengeTasksCommand;
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils;
import com.magicmicky.habitrpgwrapper.lib.models.Challenge;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import org.greenrobot.eventbus.EventBus;
@ -51,6 +57,11 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
return challenges.size();
}
public void addChallange(Challenge challenge) {
challenges.add(challenge);
notifyDataSetChanged();
}
public static class ChallengeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@BindView(R.id.challenge_name)
TextView challengeName;
@ -58,8 +69,6 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
@BindView(R.id.challenge_description)
TextView challengeDescription;
@BindView(R.id.leader_label)
TextView leaderName;
@BindView(R.id.memberCountTextView)
TextView memberCountTextView;
@ -70,6 +79,18 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
@BindView(R.id.gemPrizeTextView)
TextView gemPrizeTextView;
@BindView(R.id.challenge_button_join)
Button joinButton;
@BindView(R.id.challenge_button_leave)
Button leaveButton;
@BindView(R.id.user_background_layout)
LinearLayout leaderLayout;
@BindView(R.id.user_label)
TextView leaderLabel;
private Challenge challenge;
public ChallengeViewHolder(View itemView) {
@ -78,6 +99,13 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
ButterKnife.bind(this, itemView);
itemView.setOnClickListener(this);
joinButton.setOnClickListener(this);
joinButton.setVisibility(View.INVISIBLE);
leaveButton.setOnClickListener(this);
leaveButton.setVisibility(View.INVISIBLE);
leaderLayout.setOnClickListener(this);
}
public void bind(Challenge challenge) {
@ -86,7 +114,9 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
challengeName.setText(challenge.name);
challengeDescription.setText(challenge.description);
leaderName.setText(String.format(getContext().getString(R.string.byLeader), challenge.leaderName));
DataBindingUtils.setRoundedBackgroundInt(leaderLayout, android.R.color.darker_gray);
DataBindingUtils.setForegroundTintColor(leaderLabel, android.R.color.white);
leaderLabel.setText(String.format(getContext().getString(R.string.byLeader), challenge.leaderName));
memberCountTextView.setText(challenge.memberCount + "");
@ -96,11 +126,33 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
gem_prize_layout.setVisibility(View.VISIBLE);
gemPrizeTextView.setText(challenge.prize + "");
}
if (leaveButton != null && joinButton != null) {
boolean userIdExists = challenge.user_id != null && !challenge.user_id.isEmpty();
leaveButton.setVisibility(userIdExists ? View.VISIBLE : View.INVISIBLE);
joinButton.setVisibility(userIdExists ? View.INVISIBLE : View.VISIBLE);
}
}
@Override
public void onClick(View view) {
if (challenge != null) {
if (view == leaderLayout) {
EventBus.getDefault().post(new OpenFullProfileCommand(challenge.leaderId));
} else if (view == joinButton) {
EventBus.getDefault().post(new JoinChallengeCommand(challenge.id));
} else if (view == leaveButton) {
Context context = view.getContext();
new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.challenge_leave_title))
.setMessage(String.format(context.getString(R.string.challenge_leave_text), challenge.name))
.setPositiveButton(context.getString(R.string.yes), (dialog, which) -> {
EventBus.getDefault().post(new LeaveChallengeCommand(challenge.id));
}).setNegativeButton(context.getString(R.string.no), (dialog, which) -> {
dialog.dismiss();
}).show();
} else if (challenge != null) {
// Card tapped
EventBus.getDefault().post(new ShowChallengeTasksCommand(challenge.id));
}
}

View file

@ -4,6 +4,7 @@ import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -55,7 +56,6 @@ public class ChallengeListFragment extends BaseMainFragment implements View.OnCl
super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.fragment_challengeslist, container, false);
unbinder = ButterKnife.bind(this, v);
swipeRefreshLayout.setOnRefreshListener(this);
recyclerView.setLayoutManager(new LinearLayoutManager(this.activity));
recyclerView.setAdapter(challengeAdapter);
@ -84,27 +84,14 @@ public class ChallengeListFragment extends BaseMainFragment implements View.OnCl
query = query.and(Condition.column("user_id").is(user.getId()));
}
query.async().queryList(new TransactionListener<List<Challenge>>() {
@Override
public void onResultReceived(List<Challenge> result) {
if (result.size() != 0) {
setAdapterEntries(result);
}
List<Challenge> challenges = query.queryList();
// load online challenges & save to database
onRefresh();
}
if (challenges.size() != 0) {
setAdapterEntries(challenges);
}
@Override
public boolean onReady(BaseTransaction<List<Challenge>> transaction) {
return false;
}
@Override
public boolean hasResult(BaseTransaction<List<Challenge>> transaction, List<Challenge> result) {
return result.size() != 0;
}
});
// load online challenges & save to database
onRefresh();
}
private void fetchOnlineChallenges() {
@ -146,19 +133,26 @@ public class ChallengeListFragment extends BaseMainFragment implements View.OnCl
swipeRefreshLayout.setRefreshing(false);
}
}, throwable -> {
Log.e("ChallengeListFragment", "", throwable);
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
});
}
}
private void setAdapterEntries(List<Challenge> challenges) {
challengeAdapter.setChallenges(challenges);
}
@Override
public void onClick(View v) {
}
public void addItem(Challenge challenge) {
challengeAdapter.addChallange(challenge);
}
}

View file

@ -11,10 +11,15 @@ import android.view.ViewGroup;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent;
import com.habitrpg.android.habitica.events.commands.JoinChallengeCommand;
import com.habitrpg.android.habitica.events.commands.LeaveChallengeCommand;
import com.habitrpg.android.habitica.events.commands.ShowChallengeTasksCommand;
import com.habitrpg.android.habitica.ui.activities.ChallengeDetailActivity;
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity;
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
import com.magicmicky.habitrpgwrapper.lib.models.Challenge;
import com.raizlabs.android.dbflow.sql.language.Delete;
import com.raizlabs.android.dbflow.sql.language.Select;
import com.raizlabs.android.dbflow.sql.language.Update;
import org.greenrobot.eventbus.Subscribe;
@ -30,7 +35,6 @@ public class ChallengesOverviewFragment extends BaseMainFragment {
View v = inflater.inflate(R.layout.fragment_viewpager, container, false);
viewPager = (ViewPager) v.findViewById(R.id.view_pager);
viewPager.setCurrentItem(1);
setViewPagerAdapter();
@ -81,9 +85,9 @@ public class ChallengesOverviewFragment extends BaseMainFragment {
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "My Challenges";
return getString(R.string.my_challenges);
case 1:
return "Public";
return getString(R.string.public_challenges);
}
return "";
}
@ -105,4 +109,35 @@ public class ChallengesOverviewFragment extends BaseMainFragment {
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
}
@Subscribe
public void onEvent(JoinChallengeCommand cmd){
this.apiHelper.apiService.joinChallenge(cmd.challengeId)
.compose(apiHelper.configureApiCallObserver())
.subscribe(challenge -> {
challenge.user_id = this.user.getId();
userChallengesFragment.addItem(challenge);
}, throwable -> {
});
}
@Subscribe
public void onEvent(LeaveChallengeCommand cmd){
this.apiHelper.apiService.leaveChallenge(cmd.challengeId)
.compose(apiHelper.configureApiCallObserver())
.subscribe(aVoid -> {
Challenge challenge = new Select().from(Challenge.class).byIds(cmd.challengeId).querySingle();
challenge.user_id = null;
challenge.save();
this.user.resetChallengeList();
userChallengesFragment.onRefresh();
availableChallengesFragment.onRefresh();
}, throwable -> {
});
}
}

View file

@ -261,6 +261,8 @@ public interface ApiService {
@DELETE("user/push-devices/{regId}")
Observable<Void> deletePushDevice(@Path("regId") String regId);
/* challenges api */
@GET("challenges/user")
Observable<ArrayList<Challenge>> getUserChallenges();
@ -270,6 +272,12 @@ public interface ApiService {
@GET("challenges/{challengeId}")
Observable<Challenge> getChallenge(@Path("challengeId") String challengeId);
@POST("challenges/{challengeId}/join")
Observable<Challenge> joinChallenge(@Path("challengeId") String challengeId);
@POST("challenges/{challengeId}/leave")
Observable<Void> leaveChallenge(@Path("challengeId") String challengeId);
//DEBUG: These calls only work on a local development server
@POST("debug/add-ten-gems")

View file

@ -126,6 +126,10 @@ public class HabitRPGUser extends BaseModel {
this.challengeList = challenges;
}
public void resetChallengeList(){
challengeList = null;
}
public Preferences getPreferences() {
return preferences;
}

View file

@ -40,6 +40,7 @@ public class ChallengeDeserializer implements JsonDeserializer<Challenge> {
if (profile != null) {
challenge.leaderName = profile.get("name").getAsString();
challenge.leaderId = leaderObj.get("id").getAsString();
}
}
}