Задержка Firebase Timestamp android, вызывающая ошибку sh, когда первый документ добавляется в коллекцию в первый раз. (Временная метка по порядку) - PullRequest
0 голосов
/ 11 июля 2020

Я использовал Firebase Firestore для создания представления ресайклера в реальном времени с разбивкой на страницы, но проблема в том, что я пытаюсь упорядочить документы, используя метку времени. Когда я пытаюсь добавить новый документ, приложение вылетает. Это происходит только при добавлении первого документа в коллекцию, после этого он будет работать нормально.

Это ошибка, которую я получаю:

java.lang.IllegalArgumentException: Invalid query. You are trying to start or end a query using a document for which the field 'date_posted' is an uncommitted server timestamp. (Since the value of this field is unknown, you cannot start/end a query with it.)
    at com.google.firebase.firestore.Query.boundFromDocumentSnapshot(com.google.firebase:firebase-firestore@@21.4.3:758)
    at com.google.firebase.firestore.Query.startAfter(com.google.firebase:firebase-firestore@@21.4.3:648)
    at com.project.alihammoud.foodreviewlb.BottomSheetFragmentComment.loadMore(BottomSheetFragmentComment.java:251)
    at com.project.alihammoud.foodreviewlb.BottomSheetFragmentComment$2.onScrolled(BottomSheetFragmentComment.java:191)
    at androidx.recyclerview.widget.RecyclerView.dispatchOnScrolled(RecyclerView.java:5173)

Я получаю это, когда я отправляю документ в базу данных firestore, он получает метку времени с сервера firestore. Я знаю, что есть задержка в назначении метки времени, поэтому я попытался добавить ожидание перед обновлением, но это не сработало. Есть идеи, что может сработать, чтобы избежать этой проблемы?

Вот мой код:


public class BottomSheetFragmentComment extends BottomSheetDialogFragment {
    private Toolbar toolbar;
    private String document_id;

    private String currentUserID;
    private FirebaseAuth auth;
    private FirebaseUser currentUser;
    private FirebaseFirestore db;
    private RecyclerView recyclerView;
    private List<CommentInfo> comments_list;
    private CommentsAdapter commentsAdapter;
    private ImageButton comment_button;
    private EditText comment_text;
    private DocumentSnapshot lastVisible;
    private Boolean isFirstPageFirstLoad = true;
    private CommentID commentID;

    public BottomSheetFragmentComment(){

    }


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.bottom_sheet_comments,container,false);

        document_id = this.getArguments().getString("docID");


        auth = FirebaseAuth.getInstance();
        currentUser = auth.getCurrentUser();
        currentUserID = currentUser.getUid();
        db = FirebaseFirestore.getInstance();

        comments_list = new ArrayList<>();
        commentsAdapter = new CommentsAdapter(comments_list);

        Query firstQuery =  db.collection("Reviews").document(document_id).collection("Comments")
                .orderBy("date_posted", Query.Direction.DESCENDING)
                .limit(20);

        firstQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot documentSnapshot, @Nullable FirebaseFirestoreException e) {
                if (e == null){
                    if (!documentSnapshot.isEmpty()){

                        if (isFirstPageFirstLoad){
                            lastVisible = documentSnapshot.getDocuments().get(documentSnapshot.size()-1);
                        }

                        for (DocumentChange documentChange: documentSnapshot.getDocumentChanges()){

                            if (documentChange.getType() == DocumentChange.Type.ADDED){

                                String commentID = documentChange.getDocument().getId();

                                CommentInfo commentAdded = documentChange.getDocument().toObject(CommentInfo.class);

                                if (isFirstPageFirstLoad){
                                    comments_list.add(commentAdded);
                                }
                                else {
                                    comments_list.add(0,commentAdded);
                                }


                                commentsAdapter.notifyDataSetChanged();

                            }

                            else if (documentChange.getType() == DocumentChange.Type.REMOVED){

                                comments_list.remove(documentChange.getOldIndex());
                                commentsAdapter.notifyItemRemoved(documentChange.getOldIndex());

                            }

                            else if (documentChange.getType() == DocumentChange.Type.MODIFIED){

                                CommentInfo commentModified = documentChange.getDocument().toObject(CommentInfo.class);
                                if (documentChange.getOldIndex() == documentChange.getNewIndex()) {
                                    // Item changed but remained in same position
                                    comments_list.set(documentChange.getOldIndex(),commentModified);
                                    commentsAdapter.notifyItemChanged(documentChange.getOldIndex());
                                }else {
                                    // Item changed and changed position
                                    comments_list.remove(documentChange.getOldIndex());
                                    comments_list.add(documentChange.getNewIndex(),commentModified);
                                    commentsAdapter.notifyItemMoved(documentChange.getOldIndex(),documentChange.getNewIndex());
                                }

                                commentsAdapter.notifyDataSetChanged();

                            }
                        }

                        isFirstPageFirstLoad = false;
                    }


                }

            }
        });



       // db.collection("Reviews").document(document_id).collection("Comments")

        if (currentUser != null){

            //setUpRecyclerView();
            recyclerView = view.findViewById(R.id.recycle_view);
            recyclerView.setHasFixedSize(true);
            LinearLayoutManager mLayoutManager = new LinearLayoutManager(getContext());
            mLayoutManager.setReverseLayout(true);
           // mLayoutManager.setStackFromEnd(true);
            recyclerView.setLayoutManager(mLayoutManager);
            recyclerView.setAdapter(commentsAdapter);

            recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                }

                @Override
                public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    Boolean reachedBottom = !recyclerView.canScrollVertically(-5);

                    if (reachedBottom){

                        String desc = lastVisible.getString("comment");
                        Toast.makeText(getContext(),"Reached: " + desc ,Toast.LENGTH_SHORT).show();
                        loadMore();

                       /* final Handler handler = new Handler();
                        handler.postDelayed(new Runnable() {
                            @Override
                            public void run() {

                            }
                        },5000);*/
                    }
                }
            });


        }


        toolbar = view.findViewById(R.id.toolbar);
        toolbar.setTitle("Comments");
        toolbar.setTitleTextColor(Color.BLACK);

        comment_text = view.findViewById(R.id.comment_text);

        comment_button = view.findViewById(R.id.comment_button);
        comment_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                db.collection("Reviews").document(document_id).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                        if (task.getResult().exists()){
                            DocumentReference newComment = db.collection("Reviews").document(document_id).collection("Comments").document();
                            CommentInfo commentInfo = new CommentInfo();
                            commentInfo.setUser_id(currentUserID);
                            commentInfo.setComment(comment_text.getText().toString().trim());
                            newComment.set(commentInfo);
                            comment_text.getText().clear();
                        }
                        else {
                            Toast.makeText(getContext(), "This review has been removed, please refresh your feed.", Toast.LENGTH_LONG).show();
                        }

                    }
                });

            }
        });

        return view;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
    }

    public  void loadMore(){
        Query nextQuery =  db.collection("Reviews").document(document_id).collection("Comments")
                .orderBy("date_posted", Query.Direction.DESCENDING)
                .startAfter(lastVisible)
                .limit(10);

        nextQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot documentSnapshot, @Nullable FirebaseFirestoreException e) {
                if (e == null){
                    if (!documentSnapshot.isEmpty()){

                        lastVisible = documentSnapshot.getDocuments().get(documentSnapshot.size()-1);

                        for (DocumentChange documentChange: documentSnapshot.getDocumentChanges()){

                            if (documentChange.getType() == DocumentChange.Type.ADDED){

                                String commentID = documentChange.getDocument().getId();

                                CommentInfo commentAdded = documentChange.getDocument().toObject(CommentInfo.class);
                                comments_list.add(commentAdded);
                                commentsAdapter.notifyDataSetChanged();
                            }

                            else if (documentChange.getType() == DocumentChange.Type.REMOVED){

                                comments_list.remove(documentChange.getOldIndex());
                                commentsAdapter.notifyItemRemoved(documentChange.getOldIndex());
                            }

                            else if (documentChange.getType() == DocumentChange.Type.MODIFIED){

                                // modifying

                                CommentInfo commentModified = documentChange.getDocument().toObject(CommentInfo.class);
                                if (documentChange.getOldIndex() == documentChange.getNewIndex()) {
                                    // Item changed but remained in same position
                                    comments_list.set(documentChange.getOldIndex(),commentModified);
                                    commentsAdapter.notifyItemChanged(documentChange.getOldIndex());
                                }else {
                                    // Item changed and changed position
                                    comments_list.remove(documentChange.getOldIndex());
                                    comments_list.add(documentChange.getNewIndex(),commentModified);
                                    commentsAdapter.notifyItemMoved(documentChange.getOldIndex(),documentChange.getNewIndex());
                                }

                                commentsAdapter.notifyDataSetChanged();

                            }
                        }
                    }


                }

            }
        });
    }
    @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override public void onShow(DialogInterface dialogInterface) {
                BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface;
                setupFullHeight(bottomSheetDialog);
            }
        });
        return  dialog;
    }

    private void setupFullHeight(BottomSheetDialog bottomSheetDialog) {
        FrameLayout bottomSheet = (FrameLayout) bottomSheetDialog.findViewById(R.id.design_bottom_sheet);
        BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
        ViewGroup.LayoutParams layoutParams = bottomSheet.getLayoutParams();

        int windowHeight = getWindowHeight();
        if (layoutParams != null) {
            layoutParams.height = windowHeight;
        }
        bottomSheet.setLayoutParams(layoutParams);
        behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    }
    private int getWindowHeight() {
        // Calculate window height for fullscreen use
        DisplayMetrics displayMetrics = new DisplayMetrics();
        ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.heightPixels;
    }

}


public class CommentsAdapter  extends RecyclerView.Adapter<CommentsAdapter.ViewHolder> {

private Context context;
private FirebaseFirestore db;

    public  List<CommentInfo> comments_list;

    public CommentsAdapter(List<CommentInfo> comments_list){

        this.comments_list = comments_list;

    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view_comments,parent,false);
        context = parent.getContext();



        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {

        db = FirebaseFirestore.getInstance();
        //final String commentID = comments_list.get(position)

        String comment = comments_list.get(position).getComment();
        holder.setComment(comment);

        final String commentUserID = comments_list.get(position).getUser_id();
        DocumentReference userData = db.collection("Users").document(commentUserID);
        userData.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
            @Override
            public void onComplete(@NonNull Task<DocumentSnapshot> task) {
                if (task.isSuccessful()) {
                    if (task.getResult().exists()) {

                        String firstname = task.getResult().getString("first_name");
                        String lastname = task.getResult().getString("last_name");
                        String userPicture = task.getResult().getString("profile_picture");

                        holder.setUserData(firstname,lastname,userPicture);

                    }
                }
            }
        });

    }

    @Override
    public int getItemCount() {
        return comments_list.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder{
        private View mView;
        private TextView comment, first_name, last_name;
        CircleImageView users_profile_picture;

        public ViewHolder(@NonNull final View itemView) {
            super(itemView);

            mView = itemView;



        }

        public void setComment(String CommentText){

            comment = mView.findViewById(R.id.comment);
            comment.setText(CommentText);
        }


        private void setUserData(String firstName, String lastName, String profilePicture){
            first_name = mView.findViewById(R.id.first_name);
            last_name = mView.findViewById(R.id.last_name);
            users_profile_picture = mView.findViewById(R.id.users_profile_picture);

            first_name.setText(firstName);
            last_name.setText(lastName);

            if (profilePicture != null){

                Glide.with(context).load(profilePicture).into(users_profile_picture);
            }


        }

    }
}


@IgnoreExtraProperties
public class CommentInfo {


    private String user_id;
    private String comment;
    private @ServerTimestamp Date date_posted;


    public CommentInfo(){

    }

    public CommentInfo(String user_id, String comment, Date date_posted) {

        this.user_id = user_id;
        this.comment = comment;
        this.date_posted = date_posted;

    }
    

    public String getUser_id() {
        return user_id;
    }

    public void setUser_id(String user_id) {
        this.user_id = user_id;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Date getDate_posted() {
        return date_posted;
    }

    public void setDate_posted(Date date_posted) {
        this.date_posted = date_posted;
    }

    

}

1 Ответ

0 голосов
/ 11 июля 2020

Сообщение об ошибке:

Неверный запрос. Вы пытаетесь начать или завершить запрос, используя документ, для которого поле date_posted - это незафиксированная отметка времени сервера. (Поскольку значение этого поля неизвестно, вы не можете начинать / заканчивать запрос с его помощью.)

Итак, похоже, что это происходит из этого:

Query nextQuery =  db.collection("Reviews").document(document_id).collection("Comments")
        .orderBy("date_posted", Query.Direction.DESCENDING)
        .startAfter(lastVisible)
        .limit(10);

Из сообщение об ошибке кажется, что поле date_posted может иметь неизвестные значения, и в этом случае вы не можете запросить его.

Это происходит от:

private @ServerTimestamp Date date_posted;

Отсюда Проблема Github с несколькими комментариями членов команды Firestore, кажется, что:

разбивка на страницы и время, выданное сервером, по существу несовместимы

...