Я использовал 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;
}
}