diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index f9a8c79833f5..232bd75a01b1 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,5 +1,7 @@ 14.3 ----- +* Added search to set page parent screen + 14.2 ----- diff --git a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java index 6c3498117247..8899bbbf8ac3 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java @@ -65,6 +65,7 @@ import org.wordpress.android.ui.notifications.receivers.NotificationsPendingDraftsReceiver; import org.wordpress.android.ui.pages.PageListFragment; import org.wordpress.android.ui.pages.PageParentFragment; +import org.wordpress.android.ui.pages.PageParentSearchFragment; import org.wordpress.android.ui.pages.PagesFragment; import org.wordpress.android.ui.pages.SearchListFragment; import org.wordpress.android.ui.people.PeopleInviteFragment; @@ -490,6 +491,8 @@ public interface AppComponent extends AndroidInjector { void inject(AddContentAdapter object); + void inject(PageParentSearchFragment object); + // Allows us to inject the application without having to instantiate any modules, and provides the Application // in the app graph @Component.Builder diff --git a/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java b/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java index 3161fff7f39f..23efd0d8add0 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/ViewModelModule.java @@ -43,6 +43,7 @@ import org.wordpress.android.viewmodel.history.HistoryViewModel; import org.wordpress.android.viewmodel.main.WPMainActivityViewModel; import org.wordpress.android.viewmodel.pages.PageListViewModel; +import org.wordpress.android.viewmodel.pages.PageParentSearchViewModel; import org.wordpress.android.viewmodel.pages.PageParentViewModel; import org.wordpress.android.viewmodel.pages.PagesViewModel; import org.wordpress.android.viewmodel.pages.SearchListViewModel; @@ -282,6 +283,11 @@ abstract class ViewModelModule { @ViewModelKey(PostSignupInterstitialViewModel.class) abstract ViewModel postSignupInterstitialViewModel(PostSignupInterstitialViewModel viewModel); + @Binds + @IntoMap + @ViewModelKey(PageParentSearchViewModel.class) + abstract ViewModel pageParentSearchViewModel(PageParentSearchViewModel viewModel); + @Binds abstract ViewModelProvider.Factory provideViewModelFactory(ViewModelFactory viewModelFactory); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentFragment.kt index 1144c4cdaa2e..c519716bb81d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentFragment.kt @@ -8,15 +8,20 @@ import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater import android.view.MenuItem +import android.view.MenuItem.OnActionExpandListener import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.appcompat.widget.SearchView import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.android.synthetic.main.pages_list_fragment.* +import kotlinx.android.synthetic.main.page_parent_fragment.* +import kotlinx.android.synthetic.main.pages_list_fragment.recyclerView import kotlinx.coroutines.CoroutineScope import org.wordpress.android.R import org.wordpress.android.WordPress @@ -37,8 +42,10 @@ class PageParentFragment : Fragment() { private var linearLayoutManager: LinearLayoutManager? = null private var saveButton: MenuItem? = null + private lateinit var searchAction: MenuItem private var pageId: Long? = null + private var restorePreviousSearch = false companion object { fun newInstance(): PageParentFragment { @@ -54,6 +61,7 @@ class PageParentFragment : Fragment() { viewModel.onSaveButtonTapped() return true } + return super.onOptionsItemSelected(item) } @@ -76,10 +84,63 @@ class PageParentFragment : Fragment() { saveButton = menu.findItem(R.id.save_parent) viewModel.isSaveButtonVisible.value?.let { saveButton?.isVisible = it } + searchAction = checkNotNull(menu.findItem(R.id.action_search)) { + "Menu does not contain mandatory search item" + } + + initializeSearchView() + } + + private fun initializeSearchView() { + searchAction.setOnActionExpandListener(object : OnActionExpandListener { + override fun onMenuItemActionExpand(item: MenuItem?): Boolean { + viewModel.onSearchExpanded(restorePreviousSearch) + return true + } + + override fun onMenuItemActionCollapse(item: MenuItem?): Boolean { + viewModel.onSearchCollapsed() + return true + } + }) + + val searchView = searchAction.actionView as SearchView + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + viewModel.onSearch(query) + return true + } + + override fun onQueryTextChange(newText: String): Boolean { + if (restorePreviousSearch) { + restorePreviousSearch = false + searchView.setQuery(viewModel.lastSearchQuery, false) + } else { + viewModel.onSearch(newText) + } + return true + } + }) + + val searchEditFrame = searchAction?.actionView.findViewById(R.id.search_edit_frame) + (searchEditFrame.layoutParams as LinearLayout.LayoutParams) + .apply { this.leftMargin = DisplayUtils.dpToPx(activity, -8) } + + viewModel.isSearchExpanded.observe(this, Observer { + if (it == true) { + showSearchList(searchAction) + } else { + hideSearchList(searchAction) + } + }) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.pages_list_fragment, container, false) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.page_parent_fragment, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -92,11 +153,11 @@ class PageParentFragment : Fragment() { (nonNullActivity.application as? WordPress)?.component()?.inject(this) - initializeViews(savedInstanceState) - initializeViewModels(nonNullPageId, savedInstanceState == null) + initializeViews(nonNullActivity, savedInstanceState) + initializeViewModels(nonNullActivity, nonNullPageId, savedInstanceState == null) } - private fun initializeViews(savedInstanceState: Bundle?) { + private fun initializeViews(activity: FragmentActivity, savedInstanceState: Bundle?) { val layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false) savedInstanceState?.getParcelable(listStateKey)?.let { layoutManager.onRestoreInstanceState(it) @@ -105,17 +166,30 @@ class PageParentFragment : Fragment() { linearLayoutManager = layoutManager recyclerView.layoutManager = linearLayoutManager recyclerView.addItemDecoration(RecyclerItemDecoration(0, DisplayUtils.dpToPx(activity, 1))) + + val searchFragment = PageParentSearchFragment.newInstance() + activity.supportFragmentManager + .beginTransaction() + .replace(R.id.frameSearch, searchFragment) + .commit() } - private fun initializeViewModels(pageId: Long, isFirstStart: Boolean) { - viewModel = ViewModelProviders.of(this, viewModelFactory).get(PageParentViewModel::class.java) + private fun initializeViewModels( + activity: FragmentActivity, + pageId: Long, + isFirstStart: Boolean + ) { + viewModel = ViewModelProviders.of(activity, viewModelFactory) + .get(PageParentViewModel::class.java) setupObservers() if (isFirstStart) { - val site = activity?.intent?.getSerializableExtra(WordPress.SITE) as SiteModel? + val site = activity.intent?.getSerializableExtra(WordPress.SITE) as SiteModel? val nonNullSite = checkNotNull(site) viewModel.start(nonNullSite, pageId) + } else { + restorePreviousSearch = true } } @@ -148,4 +222,24 @@ class PageParentFragment : Fragment() { } adapter.update(pages) } + + private fun hideSearchList(myActionMenuItem: MenuItem) { + recyclerView.visibility = View.VISIBLE + frameSearch.visibility = View.GONE + if (myActionMenuItem.isActionViewExpanded) { + myActionMenuItem.collapseActionView() + } + /**Force the recyclerview to redraw if selection has changed while in search mode*/ + if (viewModel.currentParent != viewModel.initialParent) { + recyclerView.adapter?.notifyDataSetChanged() + } + } + + private fun showSearchList(myActionMenuItem: MenuItem) { + frameSearch.visibility = View.VISIBLE + recyclerView.visibility = View.GONE + if (!myActionMenuItem.isActionViewExpanded) { + myActionMenuItem.expandActionView() + } + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchAdapter.kt new file mode 100644 index 000000000000..24d53c297dfc --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchAdapter.kt @@ -0,0 +1,57 @@ +package org.wordpress.android.ui.pages + +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView.Adapter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.wordpress.android.R.layout +import org.wordpress.android.ui.pages.PageItem.ParentPage +import org.wordpress.android.ui.pages.PageItemViewHolder.EmptyViewHolder +import org.wordpress.android.ui.pages.PageItemViewHolder.PageDividerViewHolder +import org.wordpress.android.ui.pages.PageItemViewHolder.PageParentViewHolder + +class PageParentSearchAdapter( + private val onParentSelected: (ParentPage) -> Unit, + private val uiScope: CoroutineScope +) : Adapter() { + private val items = mutableListOf() + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PageItemViewHolder { + return when (viewType) { + PageItem.Type.PARENT.viewType -> PageParentViewHolder(parent, + this::selectParent, + layout.page_parent_list_item) + PageItem.Type.TOP_LEVEL_PARENT.viewType -> PageParentViewHolder(parent, + this::selectParent, + layout.page_parent_top_level_item) + PageItem.Type.DIVIDER.viewType -> PageDividerViewHolder(parent) + PageItem.Type.EMPTY.viewType -> EmptyViewHolder(parent) { } + else -> throw Throwable("Unexpected view type") + } + } + + private fun selectParent(parent: ParentPage) { + onParentSelected(parent) + uiScope.launch { + delay(200) // let the selection animation play out before refreshing the list + notifyDataSetChanged() + } + } + override fun getItemCount(): Int = items.size + + override fun getItemViewType(position: Int): Int { + return items[position].type.viewType + } + + override fun onBindViewHolder(holder: PageItemViewHolder, position: Int) { + holder.onBind(items[position]) + } + + fun update(result: List) { + val diffResult = DiffUtil.calculateDiff(PageItemDiffUtil(items.toList(), result)) + items.clear() + items.addAll(result) + diffResult.dispatchUpdatesTo(this) + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchFragment.kt new file mode 100644 index 000000000000..fb5428f980e6 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/pages/PageParentSearchFragment.kt @@ -0,0 +1,104 @@ +package org.wordpress.android.ui.pages + +import android.os.Bundle +import android.os.Parcelable +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProviders +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.pages_list_fragment.* +import kotlinx.coroutines.CoroutineScope +import org.wordpress.android.R +import org.wordpress.android.WordPress +import org.wordpress.android.modules.UI_SCOPE +import org.wordpress.android.util.DisplayUtils +import org.wordpress.android.viewmodel.pages.PageParentSearchViewModel +import org.wordpress.android.viewmodel.pages.PageParentViewModel +import org.wordpress.android.widgets.RecyclerItemDecoration +import javax.inject.Inject +import javax.inject.Named + +class PageParentSearchFragment : Fragment() { + @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + private lateinit var viewModel: PageParentSearchViewModel + @field:[Inject Named(UI_SCOPE)] lateinit var uiScope: CoroutineScope + private var linearLayoutManager: LinearLayoutManager? = null + + private val listStateKey = "list_state" + + companion object { + fun newInstance(): PageParentSearchFragment { + return PageParentSearchFragment() + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.pages_list_fragment, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val nonNullActivity = checkNotNull(activity) + (nonNullActivity.application as? WordPress)?.component()?.inject(this) + + initializeViews(savedInstanceState) + initializeViewModels(nonNullActivity) + } + + override fun onSaveInstanceState(outState: Bundle) { + linearLayoutManager?.let { + outState.putParcelable(listStateKey, it.onSaveInstanceState()) + } + super.onSaveInstanceState(outState) + } + + private fun initializeViewModels(activity: FragmentActivity) { + val pageParentViewModel = ViewModelProviders.of(activity, viewModelFactory) + .get(PageParentViewModel::class.java) + + viewModel = ViewModelProviders.of(this, viewModelFactory) + .get(PageParentSearchViewModel::class.java) + viewModel.start(pageParentViewModel) + + setupObservers() + } + + private fun initializeViews(savedInstanceState: Bundle?) { + val layoutManager = LinearLayoutManager(activity, RecyclerView.VERTICAL, false) + savedInstanceState?.getParcelable(listStateKey)?.let { + layoutManager.onRestoreInstanceState(it) + } + + linearLayoutManager = layoutManager + recyclerView.layoutManager = linearLayoutManager + recyclerView.addItemDecoration(RecyclerItemDecoration(0, DisplayUtils.dpToPx(activity, 1))) + } + + private fun setupObservers() { + viewModel.searchResult.observe(this, Observer { data -> + data?.let { setSearchResult(data) } + }) + } + + private fun setSearchResult(pages: List) { + val adapter: PageParentSearchAdapter + if (recyclerView.adapter == null) { + adapter = PageParentSearchAdapter( + { page -> viewModel.onParentSelected(page) }, uiScope) + recyclerView.adapter = adapter + } else { + adapter = recyclerView.adapter as PageParentSearchAdapter + } + adapter.update(pages) + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/viewmodel/pages/PageParentSearchViewModel.kt b/WordPress/src/main/java/org/wordpress/android/viewmodel/pages/PageParentSearchViewModel.kt new file mode 100644 index 000000000000..42613572df06 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/viewmodel/pages/PageParentSearchViewModel.kt @@ -0,0 +1,60 @@ +package org.wordpress.android.viewmodel.pages + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.wordpress.android.R +import org.wordpress.android.modules.UI_SCOPE +import org.wordpress.android.ui.pages.PageItem +import org.wordpress.android.ui.pages.PageItem.Empty +import org.wordpress.android.ui.pages.PageItem.ParentPage +import javax.inject.Inject +import javax.inject.Named + +class PageParentSearchViewModel +@Inject constructor( + @Named(UI_SCOPE) private val uiScope: CoroutineScope +) : ViewModel() { + private val _searchResult: MutableLiveData> = MutableLiveData() + val searchResult: LiveData> = _searchResult + + private var isStarted: Boolean = false + private lateinit var pageParentViewModel: PageParentViewModel + + fun start(pageParentViewModel: PageParentViewModel) { + this.pageParentViewModel = pageParentViewModel + + if (!isStarted) { + isStarted = true + + pageParentViewModel.searchPages.observeForever(searchObserver) + } + } + + override fun onCleared() { + pageParentViewModel.searchPages.removeObserver(searchObserver) + } + + private val searchObserver = Observer> { pageItems -> + if (pageItems != null) { + loadFoundPages(pageItems) + } else { + _searchResult.value = listOf(Empty(R.string.pages_search_suggestion, true)) + } + } + + fun onParentSelected(page: ParentPage) { + pageParentViewModel.onParentSelected(page) + } + + private fun loadFoundPages(pageItems: List) = uiScope.launch { + if (pageItems.isNotEmpty()) { + _searchResult.value = pageItems + } else { + _searchResult.value = listOf(Empty(R.string.pages_empty_search_result, true)) + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/viewmodel/pages/PageParentViewModel.kt b/WordPress/src/main/java/org/wordpress/android/viewmodel/pages/PageParentViewModel.kt index 2f63ff17c240..e3e82b498377 100644 --- a/WordPress/src/main/java/org/wordpress/android/viewmodel/pages/PageParentViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/viewmodel/pages/PageParentViewModel.kt @@ -2,9 +2,12 @@ package org.wordpress.android.viewmodel.pages import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.wordpress.android.R import org.wordpress.android.R.string import org.wordpress.android.analytics.AnalyticsTracker.Stat.PAGES_SET_PARENT_CHANGES_SAVED @@ -12,7 +15,8 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.page.PageModel import org.wordpress.android.fluxc.model.page.PageStatus.PUBLISHED import org.wordpress.android.fluxc.store.PageStore -import org.wordpress.android.modules.DEFAULT_SCOPE +import org.wordpress.android.modules.BG_THREAD +import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.pages.PageItem import org.wordpress.android.ui.pages.PageItem.Divider import org.wordpress.android.ui.pages.PageItem.Empty @@ -21,16 +25,21 @@ import org.wordpress.android.ui.pages.PageItem.Type.PARENT import org.wordpress.android.ui.pages.PageItem.Type.TOP_LEVEL_PARENT import org.wordpress.android.util.analytics.AnalyticsUtils import org.wordpress.android.viewmodel.ResourceProvider +import org.wordpress.android.viewmodel.ScopedViewModel import org.wordpress.android.viewmodel.SingleLiveEvent import javax.inject.Inject import javax.inject.Named +private const val SEARCH_DELAY = 200L +private const val SEARCH_COLLAPSE_DELAY = 500L + class PageParentViewModel @Inject constructor( private val pageStore: PageStore, private val resourceProvider: ResourceProvider, - @param:Named(DEFAULT_SCOPE) private val defaultScope: CoroutineScope -) : ViewModel() { + @Named(UI_THREAD) private val uiDispatcher: CoroutineDispatcher, + @Named(BG_THREAD) private val defaultDispatcher: CoroutineDispatcher +) : ScopedViewModel(uiDispatcher) { private val _pages: MutableLiveData> = MutableLiveData() val pages: LiveData> = _pages @@ -38,14 +47,31 @@ class PageParentViewModel val currentParent: ParentPage get() = _currentParent + private lateinit var _initialParent: ParentPage + val initialParent: ParentPage + get() = _initialParent + private val _isSaveButtonVisible = MutableLiveData() val isSaveButtonVisible: LiveData = _isSaveButtonVisible private val _saveParent = SingleLiveEvent() val saveParent: LiveData = _saveParent + private val _searchPages: MutableLiveData> = MutableLiveData() + val searchPages: LiveData> = _searchPages + + private var _lastSearchQuery = "" + val lastSearchQuery: String + get() = _lastSearchQuery + + private var searchJob: Job? = null + + private val _isSearchExpanded = MutableLiveData() + val isSearchExpanded: LiveData = _isSearchExpanded + private lateinit var site: SiteModel private var isStarted: Boolean = false + private var page: PageModel? = null fun start(site: SiteModel, pageId: Long) { @@ -61,7 +87,7 @@ class PageParentViewModel } } - private fun loadPages(pageId: Long) = defaultScope.launch { + private fun loadPages(pageId: Long) = launch(defaultDispatcher) { page = if (pageId < 0) { // negative local page ID used as a temp remote post ID for local-only pages (assigned by the PageStore) pageStore.getPageByLocalId(-pageId.toInt(), site) @@ -78,7 +104,8 @@ class PageParentViewModel ) if (page != null) { - val choices = pageStore.getPagesFromDb(site).filter { it.remoteId != pageId && it.status == PUBLISHED } + val choices = pageStore.getPagesFromDb(site) + .filter { it.remoteId != pageId && it.status == PUBLISHED } val parentChoices = choices.filter { isNotChild(it, choices) } if (parentChoices.isNotEmpty()) { parents.add(Divider(resourceProvider.getString(R.string.pages))) @@ -91,6 +118,8 @@ class PageParentViewModel _currentParent = parents.firstOrNull { it is ParentPage && it.isSelected } as? ParentPage ?: parents.first() as ParentPage + _initialParent = _currentParent + _pages.postValue(parents) } @@ -98,8 +127,7 @@ class PageParentViewModel _currentParent.isSelected = false _currentParent = page _currentParent.isSelected = true - - _isSaveButtonVisible.postValue(true) + setSaveButton() } fun onSaveButtonTapped() { @@ -128,4 +156,69 @@ class PageParentViewModel } return children + grandchildren } + + fun onSearchExpanded(restorePreviousSearch: Boolean) { + if (isSearchExpanded.value != true) { + if (!restorePreviousSearch) { + clearSearch() + } + _isSearchExpanded.value = true + _isSaveButtonVisible.postValue(false) + } + } + + fun onSearchCollapsed() { + _isSearchExpanded.value = false + clearSearch() + setSaveButton() + + launch { + delay(SEARCH_COLLAPSE_DELAY) + } + } + + private fun clearSearch() { + _lastSearchQuery = "" + _searchPages.postValue(null) + } + + fun onSearch(searchQuery: String, delay: Long = SEARCH_DELAY) { + searchJob?.cancel() + if (searchQuery.isNotEmpty()) { + searchJob = launch { + delay(delay) + searchJob = null + if (isActive) { + _lastSearchQuery = searchQuery + val result = search(searchQuery) + _searchPages.postValue(result) + } + } + } else { + _isSaveButtonVisible.postValue(false) + clearSearch() + } + } + + private suspend fun search( + searchQuery: String + ): List = withContext(defaultDispatcher) { + _pages.value?.let { + if (it.isNotEmpty()) return@withContext it + .filterIsInstance(ParentPage::class.java) + .filter { parentPage -> parentPage.id != 0L } + .filter { parentPage -> + parentPage.title.contains(searchQuery, true) + } + } + return@withContext mutableListOf() + } + + private fun setSaveButton() { + if (_currentParent == _initialParent) { + _isSaveButtonVisible.postValue(false) + } else { + _isSaveButtonVisible.postValue(true) + } + } } diff --git a/WordPress/src/main/res/layout/page_parent_fragment.xml b/WordPress/src/main/res/layout/page_parent_fragment.xml new file mode 100644 index 000000000000..c25ea7036c28 --- /dev/null +++ b/WordPress/src/main/res/layout/page_parent_fragment.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/WordPress/src/main/res/menu/page_parent_menu.xml b/WordPress/src/main/res/menu/page_parent_menu.xml index 24f1ad3173fe..15c88ddee8a3 100644 --- a/WordPress/src/main/res/menu/page_parent_menu.xml +++ b/WordPress/src/main/res/menu/page_parent_menu.xml @@ -2,6 +2,13 @@ + > + private lateinit var viewModel: PageParentSearchViewModel + + @Before + fun setUp() { + viewModel = PageParentSearchViewModel(TEST_SCOPE) + searchPages = MutableLiveData() + whenever(pageParentViewModel.searchPages).thenReturn(searchPages) + viewModel.start(pageParentViewModel) + } + + @Test + fun `show empty item on start`() { + searchPages.value = null + + assertThat(viewModel.searchResult.value).containsOnly(Empty(string.pages_search_suggestion, true)) + } + + @Test + fun `show no matches on empty search results`() { + searchPages.value = mutableListOf(Empty(string.pages_empty_search_result, false)) + + assertThat(viewModel.searchResult.value).containsOnly(Empty(string.pages_empty_search_result, false)) + } + + @Test + fun `passes page to page view model on parent radio button selected`() { + val clickedPage = PageItem.ParentPage(id = 1, title = "title", isSelected = true, type = PageItem.Type.PARENT) + + viewModel.onParentSelected(clickedPage) + + verify(pageParentViewModel).onParentSelected(clickedPage) + } +}