Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: browse page can search for books #115

Merged
merged 4 commits into from
Nov 14, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.cmput301f20t21.bookfriends.R;
import com.cmput301f20t21.bookfriends.entities.AvailableBook;
import com.cmput301f20t21.bookfriends.entities.Book;
import com.cmput301f20t21.bookfriends.enums.BOOK_ERROR;

import java.util.List;
Expand All @@ -33,7 +31,6 @@ public class BrowseFragment extends Fragment {
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
private SearchView searchView;
private FragmentManager fragmentManager;

/**
*
Expand All @@ -48,7 +45,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
@Nullable Bundle savedInstanceState) {
vm = new ViewModelProvider(this).get(BrowseViewModel.class);
setHasOptionsMenu(true);
inflateSearchedList();
View view = inflater.inflate(R.layout.fragment_browse, container, false);
return view;
}
Expand Down Expand Up @@ -80,8 +76,19 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.browse_search_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
searchView = (SearchView) menu.findItem(R.id.book_search_bar).getActionView();
}
private void inflateSearchedList() {
fragmentManager = getChildFragmentManager();
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
vm.filterBookWithKeyword(query);
return false;
}

@Override
public boolean onQueryTextChange(String newText) {
vm.filterBookWithKeyword(newText);
return false;
c25vdw marked this conversation as resolved.
Show resolved Hide resolved
}
});

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.cmput301f20t21.bookfriends.ui.browse;


import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

Expand All @@ -15,12 +17,22 @@

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class BrowseViewModel extends ViewModel {
// exposed live data
private final MutableLiveData<BOOK_ERROR> errorMessage = new MutableLiveData<>();
private final MutableLiveData<List<AvailableBook>> books = new MutableLiveData<>(new ArrayList<>());
private MutableLiveData<BOOK_ERROR> errorMessage = new MutableLiveData<>();
private final MutableLiveData<String> searchQuery = new MutableLiveData<>(""); // the updated search query we are using
// the updated/filtered/calculated book results based on changed books and/or searchQuery
private final MediatorLiveData<List<AvailableBook>> searchedBooks = new MediatorLiveData<>();

// raw book data
private final List<AvailableBook> bookData = books.getValue();
// need this reference to keep adapter referring to the same array in memory or else it won't update
private final List<AvailableBook> searchedBookData = new ArrayList<>();
c25vdw marked this conversation as resolved.
Show resolved Hide resolved

private final IAuthRepository authRepository;
private final IRequestRepository requestRepository;
private final IBookRepository bookRepository;
Expand All @@ -36,17 +48,67 @@ public BrowseViewModel(IAuthRepository authRepository, IRequestRepository reques
this.requestRepository = requestRepository;
this.bookRepository = bookRepository;

setupSearchedBooks(); // init the searched books before we fetch anything
fetchBooks();
}

public MutableLiveData<List<AvailableBook>> getBooks() {
return books;
/**
* create the searched books live data by making it listens to two source of changes:
* 1. the original books list: any changes of the books will trigger it to re-calculate results
* 2. the search keyword/query: changes of query should filter the searched books, too
*/
private void setupSearchedBooks() {
searchedBooks.setValue(searchedBookData);
searchedBooks.addSource(searchQuery, keyword -> setSearchedBooksByKeywordAndBookData(keyword, bookData)); // keyword is new
searchedBooks.addSource(books, bookData -> setSearchedBooksByKeywordAndBookData(searchQuery.getValue(), bookData)); // bookData is new (this is a local one!)
}

/**
* filter the original bookData (or the updated bookData) with the keyword (or the updated one)
* and set that value to searchedBookData and its live data wrapper
*
* @param keyword
* @param bookData
*/
private void setSearchedBooksByKeywordAndBookData(String keyword, List<AvailableBook> bookData) {
c25vdw marked this conversation as resolved.
Show resolved Hide resolved
Function<String, Boolean> contains = ifNotNullAndContains(keyword);
List<AvailableBook> newBookData = bookData
.stream()
.filter(book -> contains.apply(book.getTitle()) ||
contains.apply(book.getAuthor()) ||
contains.apply(book.getOwner()))
c25vdw marked this conversation as resolved.
Show resolved Hide resolved
.collect(Collectors.toList());
searchedBookData.clear();
searchedBookData.addAll(newBookData);
searchedBooks.setValue(searchedBookData);
}

private Function<String, Boolean> ifNotNullAndContains(String keyword) {
return (attribute) -> {
if (attribute == null || keyword == null) return true;
return attribute.contains(keyword);
};
}

public LiveData<List<AvailableBook>> getBooks() {
return searchedBooks;
}

public MutableLiveData<BOOK_ERROR> getErrorMessage() {
public LiveData<BOOK_ERROR> getErrorMessage() {
return errorMessage;
}

/**
* US 03.01.01
* As a borrower, I want to specify a keyword, and search for all books that are not currently accepted or borrowed whose **description** contains the keyword.
* NOTE: description here interpreted as all the basic information of the book: title, author, owner
*
* @param keyword the keyword to search with
*/
public void filterBookWithKeyword(String keyword) {
searchQuery.setValue(keyword); // will trigger update routine for searchedBooks
}

private void fetchBooks() {
// get logged in user's username
String username = authRepository.getCurrentUser().getUsername();
Expand Down