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.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/card_view" android:id="@+id/card_view"
style="@style/CardView.Default" style="@style/CardView.Default"
android:clickable="true"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="5dp"> android:layout_margin="5dp"
android:clickable="true">
<LinearLayout <LinearLayout
style="@style/CardContent.Compact" style="@style/CardContent.Compact"
@ -17,46 +17,86 @@
<TextView <TextView
android:id="@+id/challenge_name" android:id="@+id/challenge_name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:textStyle="bold" android:layout_height="wrap_content"
android:layout_height="wrap_content" /> android:textStyle="bold" />
<TextView <TextView
android:id="@+id/challenge_description" android:id="@+id/challenge_description"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:lines="3"/> android:maxLines="3" />
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:baselineAligned="false" android:baselineAligned="false"
android:weightSum="1.0"> android:weightSum="1.0">
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="30dp"
android:layout_weight="0.5"> android:layout_weight="0.5">
<LinearLayout
<TextView android:id="@+id/user_background_layout"
android:id="@+id/leader_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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" <TextView
android:lines="1" /> 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>
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="30dp"
android:layout_weight="0.5" 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 <ImageView
android:layout_width="20dp" android:layout_width="20dp"
android:layout_height="20dp" android:layout_height="20dp"
android:layout_gravity="center"
android:src="@drawable/ic_people_black_24dp" /> android:src="@drawable/ic_people_black_24dp" />
<TextView <TextView
@ -77,37 +117,36 @@
<LinearLayout <LinearLayout
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:baselineAligned="false" android:baselineAligned="false">
android:gravity="end"
android:weightSum="1.0">
<LinearLayout <LinearLayout
android:id="@+id/gem_prize_layout" android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0.5" android:layout_weight="1">
android:gravity="end">
<ImageView <Button
android:layout_width="20dp" android:id="@+id/challenge_button_leave"
android:layout_height="20dp" style="@style/Widget.AppCompat.Button.Borderless"
android:src="@drawable/ic_header_gem" />
<TextView
android:id="@+id/gemPrizeTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_gravity="center" android:gravity="start|fill_vertical"
android:ellipsize="middle" android:text="@string/leave"
android:lines="1" android:textColor="@color/md_red_400" />
android:paddingLeft="5dp"
android:paddingStart="5dp"
android:text="0" />
</LinearLayout> </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> </LinearLayout>
</android.support.v7.widget.CardView> </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="AudioTheme_summary">Change Habitica\'s Audio Theme</string>
<string name="byLeader">by %s</string> <string name="byLeader">by %s</string>
<string name="challenge_details">Challenge Details</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> </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.os.Bundle;
import android.support.design.widget.TabLayout; import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar; 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.APIHelper;
import com.habitrpg.android.habitica.HabiticaApplication; import com.habitrpg.android.habitica.HabiticaApplication;
import com.habitrpg.android.habitica.R; import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.components.AppComponent; 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.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeTasksFragment;
import com.magicmicky.habitrpgwrapper.lib.models.Challenge;
import javax.inject.Inject; import javax.inject.Inject;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife;
public class ChallengeDetailActivity extends BaseActivity { public class ChallengeDetailActivity extends BaseActivity {
@ -73,4 +78,51 @@ public class ChallengeDetailActivity extends BaseActivity {
protected void injectActivity(AppComponent component) { protected void injectActivity(AppComponent component) {
component.inject(this); 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; 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.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import com.habitrpg.android.habitica.R; 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.events.commands.ShowChallengeTasksCommand;
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils;
import com.magicmicky.habitrpgwrapper.lib.models.Challenge; import com.magicmicky.habitrpgwrapper.lib.models.Challenge;
import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
@ -51,6 +57,11 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
return challenges.size(); return challenges.size();
} }
public void addChallange(Challenge challenge) {
challenges.add(challenge);
notifyDataSetChanged();
}
public static class ChallengeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public static class ChallengeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@BindView(R.id.challenge_name) @BindView(R.id.challenge_name)
TextView challengeName; TextView challengeName;
@ -58,8 +69,6 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
@BindView(R.id.challenge_description) @BindView(R.id.challenge_description)
TextView challengeDescription; TextView challengeDescription;
@BindView(R.id.leader_label)
TextView leaderName;
@BindView(R.id.memberCountTextView) @BindView(R.id.memberCountTextView)
TextView memberCountTextView; TextView memberCountTextView;
@ -70,6 +79,18 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
@BindView(R.id.gemPrizeTextView) @BindView(R.id.gemPrizeTextView)
TextView 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; private Challenge challenge;
public ChallengeViewHolder(View itemView) { public ChallengeViewHolder(View itemView) {
@ -78,6 +99,13 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
ButterKnife.bind(this, itemView); ButterKnife.bind(this, itemView);
itemView.setOnClickListener(this); 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) { public void bind(Challenge challenge) {
@ -86,7 +114,9 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
challengeName.setText(challenge.name); challengeName.setText(challenge.name);
challengeDescription.setText(challenge.description); 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 + ""); memberCountTextView.setText(challenge.memberCount + "");
@ -96,11 +126,33 @@ public class ChallengesListViewAdapter extends RecyclerView.Adapter<ChallengesLi
gem_prize_layout.setVisibility(View.VISIBLE); gem_prize_layout.setVisibility(View.VISIBLE);
gemPrizeTextView.setText(challenge.prize + ""); 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 @Override
public void onClick(View view) { 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)); 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.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -55,7 +56,6 @@ public class ChallengeListFragment extends BaseMainFragment implements View.OnCl
super.onCreateView(inflater, container, savedInstanceState); super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.fragment_challengeslist, container, false); View v = inflater.inflate(R.layout.fragment_challengeslist, container, false);
unbinder = ButterKnife.bind(this, v); unbinder = ButterKnife.bind(this, v);
swipeRefreshLayout.setOnRefreshListener(this);
recyclerView.setLayoutManager(new LinearLayoutManager(this.activity)); recyclerView.setLayoutManager(new LinearLayoutManager(this.activity));
recyclerView.setAdapter(challengeAdapter); 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 = query.and(Condition.column("user_id").is(user.getId()));
} }
query.async().queryList(new TransactionListener<List<Challenge>>() { List<Challenge> challenges = query.queryList();
@Override
public void onResultReceived(List<Challenge> result) {
if (result.size() != 0) {
setAdapterEntries(result);
}
// load online challenges & save to database if (challenges.size() != 0) {
onRefresh(); setAdapterEntries(challenges);
} }
@Override // load online challenges & save to database
public boolean onReady(BaseTransaction<List<Challenge>> transaction) { onRefresh();
return false;
}
@Override
public boolean hasResult(BaseTransaction<List<Challenge>> transaction, List<Challenge> result) {
return result.size() != 0;
}
});
} }
private void fetchOnlineChallenges() { private void fetchOnlineChallenges() {
@ -146,19 +133,26 @@ public class ChallengeListFragment extends BaseMainFragment implements View.OnCl
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
} }
}, throwable -> { }, throwable -> {
Log.e("ChallengeListFragment", "", throwable);
if (swipeRefreshLayout != null) {
swipeRefreshLayout.setRefreshing(false);
}
}); });
} }
} }
private void setAdapterEntries(List<Challenge> challenges) { private void setAdapterEntries(List<Challenge> challenges) {
challengeAdapter.setChallenges(challenges); challengeAdapter.setChallenges(challenges);
} }
@Override @Override
public void onClick(View v) { 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.R;
import com.habitrpg.android.habitica.components.AppComponent; 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.events.commands.ShowChallengeTasksCommand;
import com.habitrpg.android.habitica.ui.activities.ChallengeDetailActivity; 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.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; import org.greenrobot.eventbus.Subscribe;
@ -30,7 +35,6 @@ public class ChallengesOverviewFragment extends BaseMainFragment {
View v = inflater.inflate(R.layout.fragment_viewpager, container, false); View v = inflater.inflate(R.layout.fragment_viewpager, container, false);
viewPager = (ViewPager) v.findViewById(R.id.view_pager); viewPager = (ViewPager) v.findViewById(R.id.view_pager);
viewPager.setCurrentItem(1);
setViewPagerAdapter(); setViewPagerAdapter();
@ -81,9 +85,9 @@ public class ChallengesOverviewFragment extends BaseMainFragment {
public CharSequence getPageTitle(int position) { public CharSequence getPageTitle(int position) {
switch (position) { switch (position) {
case 0: case 0:
return "My Challenges"; return getString(R.string.my_challenges);
case 1: case 1:
return "Public"; return getString(R.string.public_challenges);
} }
return ""; return "";
} }
@ -105,4 +109,35 @@ public class ChallengesOverviewFragment extends BaseMainFragment {
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent); 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}") @DELETE("user/push-devices/{regId}")
Observable<Void> deletePushDevice(@Path("regId") String regId); Observable<Void> deletePushDevice(@Path("regId") String regId);
/* challenges api */
@GET("challenges/user") @GET("challenges/user")
Observable<ArrayList<Challenge>> getUserChallenges(); Observable<ArrayList<Challenge>> getUserChallenges();
@ -270,6 +272,12 @@ public interface ApiService {
@GET("challenges/{challengeId}") @GET("challenges/{challengeId}")
Observable<Challenge> getChallenge(@Path("challengeId") String 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 //DEBUG: These calls only work on a local development server
@POST("debug/add-ten-gems") @POST("debug/add-ten-gems")

View file

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

View file

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