Zar E Ahmer
Zar E Ahmer

Reputation: 34360

Transaction not updating property in android firestore

I am trying to add a feature like or dislike to my post(Question). But it is not updating the like button image.

public class QuestionDetailActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "QuestionDetailActivity";
    private Context mContext;
    private TextView questionTitleTv;
    private RadioGroup radioLL;
    private EditText answerEt;
    private LinearLayout checkboxLL;
    private LinearLayout commentLL;

    private FirebaseFirestore db = null;
    private String questionID;

    private String userID;
    private TextView likeCountTv,favouriteCountTv;
    private boolean isLikedByUser = false,isFavouriteByUser = false;
    private DocumentReference questionRef;
    private QuestionBO questionBO;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_question_detail);

        mContext = QuestionDetailActivity.this;
        db = FirebaseFirestore.getInstance();

        userID = "V5BUeBFYR8WKOQUiBFxKrfMHK8Y2";

        questionTitleTv = findViewById(R.id.tv_question_title);
        radioLL = findViewById(R.id.ll_radio);
        answerEt = findViewById(R.id.et_answer);
        checkboxLL = findViewById(R.id.ll_checkbox);
        commentLL = findViewById(R.id.ll_comments);

        likeCountTv = findViewById(R.id.tv_like_count);
        favouriteCountTv = findViewById(R.id.tv_favourite_count);

        likeCountTv.setOnClickListener(this);
        favouriteCountTv.setOnClickListener(this);

        if (getIntent() != null) {
            questionID = getIntent().getExtras().getString("keyQuestionID", "");
        }
        questionRef = db.collection("questionCollection").document(questionID);
        if (!questionID.isEmpty()) {
            getQuestionDetail();
            ///showComments();
            updateFavouriteAndLikeIcons();
        }
    }

    private void updateFavouriteAndLikeIcons() {

        questionRef.collection("favouriteCollection").document(userID).get()
                .addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                    @Override
                    public void onSuccess(DocumentSnapshot documentSnapshot) {
                        if(documentSnapshot.exists()){
                            isFavouriteByUser = true;
                            // and make it blue
                            Drawable image = getResources().getDrawable( R.drawable.icn_fav_yes );
                            int h = image.getIntrinsicHeight();
                            int w = image.getIntrinsicWidth();
                            image.setBounds( 0, 0, w, h );
                            favouriteCountTv.setCompoundDrawables( image, null, null, null );
                        }
                    }
                });

        questionRef.collection("likeCollection").document(userID).get()
                .addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                    @Override
                    public void onSuccess(DocumentSnapshot documentSnapshot) {
                        if(documentSnapshot.exists()){
                            boolean isLiked = documentSnapshot.getBoolean(userID);
                            if(isLiked){
                                isLikedByUser = true;
                                // and make it blue
                                Drawable image = getResources().getDrawable( R.drawable.icn_like_yes );
                                int h = image.getIntrinsicHeight();
                                int w = image.getIntrinsicWidth();
                                image.setBounds( 0, 0, w, h );
                                likeCountTv.setCompoundDrawables( image, null, null, null );
                            }
                            else{//dislike
                            }
                        }
                    }
                });

    }

    private void showComments() {
        db.collection("commentCollection").whereEqualTo("questionID", questionID)
                .get()
                .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
                    @Override
                    public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
                        if (queryDocumentSnapshots.isEmpty()) {
                            Log.d(TAG, "onSuccess: LIST EMPTY");
                            return;
                        } else {
                            // Convert the whole Query Snapshot to a list
                            // of objects directly! No need to fetch each
                            // document.
                            List<CommentBO> commentList = queryDocumentSnapshots.toObjects(CommentBO.class);

                            if (commentList != null && commentList.size() > 0) {
                                for (int x = 0; x < commentList.size(); x++) {
                                    try {
                                        LinearLayout.LayoutParams childParam = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                                                LinearLayout.LayoutParams.WRAP_CONTENT);

                                        childParam.setMargins(20, 10, 0, 0);
                                        View commentByView = LayoutInflater.from(mContext).inflate(R.layout.item_comment, null);
                                        TextView tvCommenter = (TextView) commentByView.findViewById(R.id.item_tv_commenter_name);
                                        TextView tvCommenterComment = (TextView) commentByView.findViewById(R.id.item_tv_commenter_comment);
                                        TextView tvCommentDate = (TextView) commentByView.findViewById(R.id.item_tv_comment_date);

                                        tvCommenter.setText(commentList.get(x).getCommentedBy());
                                        tvCommenterComment.setText(commentList.get(x).getComment());

                                        ///tvCommentDate.setText();
                                        commentLL.addView(commentByView, childParam);

                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                });
    }

    private void getQuestionDetail() {
        questionRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                if (task.isSuccessful()) {
                    DocumentSnapshot document = task.getResult();
                    if (document.exists()) {
                        Log.d(TAG, "DocumentSnapshot data: " + document.getData());
                        questionBO = document.toObject(QuestionBO.class);
                        updateView(questionBO);
                    } else {
                        Log.d(TAG, "No such document");
                    }
                } else {
                    Log.d(TAG, "get failed with ", task.getException());
                }
            }
        });
    }

    private void updateView(QuestionBO mQuestionBO) {
        if (mQuestionBO != null) {
            questionTitleTv.setText(mQuestionBO.getTitle());

            likeCountTv.setText(mQuestionBO.getTotalLikes() + "");
            favouriteCountTv.setText(mQuestionBO.getTotalFavourites() + "");

            switch (mQuestionBO.getQuestionType()) {
                case CHECKBOX:
                    checkboxLL.setVisibility(View.VISIBLE);
                    if (mQuestionBO.getOptions() != null && mQuestionBO.getOptions().size() > 0) {
                        checkboxLL.removeAllViews();

                        for (int x = 0; x < mQuestionBO.getOptions().size(); x++) {
                            final CheckBox subCheckbox = new CheckBox(mContext);
                            subCheckbox.setText(mQuestionBO.getOptions().get(x));
                            subCheckbox.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {

                                }
                            });

                            checkboxLL.addView(subCheckbox, new LinearLayout.LayoutParams(
                                    LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
                        }
                    }
                    break;
                case RADIO:
                    radioLL.setVisibility(View.VISIBLE);
                    if (mQuestionBO.getOptions() != null && mQuestionBO.getOptions().size() > 0) {
                        radioLL.removeAllViews();

                        for (int x = 0; x < mQuestionBO.getOptions().size(); x++) {

                            final RadioButton subRadio = new RadioButton(mContext);
                            subRadio.setText(mQuestionBO.getOptions().get(x));
                            radioLL.addView(subRadio, new LinearLayout.LayoutParams(
                                    LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
                        }
                    }
                    break;
                case DESCRIPTIVE:
                    answerEt.setVisibility(View.VISIBLE);
                    break;
            }
        }
    }

    private void likeOrDislikeThisQuestion(){

        final Task<Boolean> booleanTask = db.runTransaction(new Transaction.Function<Boolean>() {

            @Nullable
            @Override
            public Boolean apply(@NonNull Transaction transaction) throws FirebaseFirestoreException {
                DocumentReference likeRef = questionRef.collection("likeCollection")
                        .document(userID);
                Map<String,Object> map = new HashMap<>();
                 DocumentSnapshot snapshot = transaction.get(likeRef);
                 if(snapshot.exists()){ // it means this is a favourite question of this user.
                     if(snapshot.getBoolean(questionID) && isLikedByUser){
                         isLikedByUser = false;
                         map.put(questionID,isLikedByUser);
                         transaction.update(likeRef,map);
                         int numberOfLikes = questionBO.getTotalLikes();
                         transaction.update(questionRef,"totalLikes",--numberOfLikes);
                         likeCountTv.setText(numberOfLikes + "");
                         return isLikedByUser;
                     }
                     else if(!snapshot.getBoolean(questionID) && !isLikedByUser){
                         isLikedByUser = true;
                         map.put(questionID,isLikedByUser);
                         transaction.update(likeRef,map);
                         int numberOfLikes = questionBO.getTotalLikes();
                         transaction.update(questionRef,"totalLikes",-++numberOfLikes);
                         likeCountTv.setText(numberOfLikes + "");
                         return isLikedByUser;
                     }
                 }
                 else {
                     isLikedByUser = !isLikedByUser;
                     map.put(questionID,isLikedByUser);// pass oposite of it..
                     transaction.set(likeRef,map);

                     int numberOfLikes = questionBO.getTotalLikes();
                     if(isLikedByUser)
                         ++numberOfLikes;
                     else
                         --numberOfLikes;
                     transaction.update(questionRef,"totalLikes",numberOfLikes);

                     likeCountTv.setText(numberOfLikes + "");
                     return isLikedByUser;
                 }
                return null;
            }
        });

        booleanTask.addOnSuccessListener(new OnSuccessListener<Boolean>() {
            @Override
            public void onSuccess(Boolean isLiked) {
                if(isLiked == null){

                }
                else if(isLiked){
                    Drawable image = getResources().getDrawable( R.drawable.icn_like_yes );
                    int h = image.getIntrinsicHeight();
                    int w = image.getIntrinsicWidth();
                    image.setBounds( 0, 0, w, h );
                    likeCountTv.setCompoundDrawables( image, null, null, null );
                }
                else{
                    Drawable image = getResources().getDrawable( R.drawable.icn_like_no );
                    int h = image.getIntrinsicHeight();
                    int w = image.getIntrinsicWidth();
                    image.setBounds( 0, 0, w, h );
                    likeCountTv.setCompoundDrawables( image, null, null, null );
                }
            }
        });
    }

    private void favouriteOrDisFavouriteQuestion(){

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.tv_like_count:
                likeOrDislikeThisQuestion();
                break;
            case R.id.tv_favourite_count:
                break;
        }
    }
}

I updated icon using updateFavouriteAndLikeIcons() for the first time. then when user clicks on like button I called likeOrDislikeThisQuestion() . Counter updating but left drawable not updating. and sometimes variable isLikedByUser become false .

If the question is already liked then likeCollection must have that Document. if that document don't exist it means User didn't like or dislike it.

My architecture of db is like

 - likeCollection
      userID (Document ID)
         questionID = true // true for like and false for dislike..

Upvotes: 0

Views: 164

Answers (1)

Teddy Vallar
Teddy Vallar

Reputation: 69

From Firebase docs :

Do not modify application state inside of your transaction functions. Doing so will introduce concurrency issues, because transaction functions can run multiple times and are not guaranteed to run on the UI thread. Instead, pass information you need out of your transaction functions.

This is what happens here, you have to run every application modifications inside a listener on your variable booleanTask. Do not change class variable in the transaction's apply method.

Upvotes: 1

Related Questions