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

issue/62-simple post search #10023

Merged
merged 54 commits into from
Jun 25, 2019
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
f3c3181
Post search menu.
khaykov May 31, 2019
2212024
Reverted id changed, added proper menu.
khaykov May 31, 2019
64838bb
Added search fragment container to post list activity.
khaykov May 31, 2019
7dca6a3
Ability to toggle search fragment from posts list.
khaykov May 31, 2019
0ab9606
Merge branch 'develop' of github.com:wordpress-mobile/WordPress-Andro…
khaykov Jun 5, 2019
6e7dd38
Fixing issues with android x.
khaykov Jun 5, 2019
f2b9d28
Updated search widget to android-x one
khaykov Jun 6, 2019
6446ed1
Marking specific instance of PostListFragment as for search, and rese…
khaykov Jun 6, 2019
a33c42a
Updated PostListActivity with new search fragment logic.
khaykov Jun 6, 2019
85636a0
Removed redundant files.
khaykov Jun 6, 2019
f7fa879
Search related strings.
khaykov Jun 7, 2019
f6b98a2
Added SEARCH as post list type.
khaykov Jun 7, 2019
90f4b08
Improved empty view handling, hide search for non wpcom sites.
khaykov Jun 7, 2019
96fc2ef
Refactored post list view model and connector.
khaykov Jun 11, 2019
40afd3a
Refactored post list activity, fixed orientation change issue.
khaykov Jun 11, 2019
7384c5c
Added analytics.
khaykov Jun 11, 2019
4e4f70d
Reverted some changes to post list activity.
khaykov Jun 11, 2019
d5cd516
Comments, refactoring.
khaykov Jun 11, 2019
8dfab00
Cleaning up, minor refactoring.
khaykov Jun 11, 2019
427ca64
Removed loading indicators for post search.
khaykov Jun 11, 2019
e09ceae
Updated release notes, removed redundant check of variable.
khaykov Jun 11, 2019
41a6f30
Merge branch 'develop' of github.com:wordpress-mobile/WordPress-Andro…
khaykov Jun 11, 2019
fa11f96
Fixed FAB apearing after device rotation with open search.
khaykov Jun 12, 2019
2c95a33
Clear post list when search query is empty.
khaykov Jun 12, 2019
2ccf37b
Fixed issue with empty view appearing during search for brief moments.
khaykov Jun 12, 2019
187d7e0
Fixed test and checkstyle errors.
khaykov Jun 13, 2019
36fb25f
Delay search progress indicator.
khaykov Jun 13, 2019
3228553
Reverted some code style changes.
khaykov Jun 13, 2019
15448fa
Fixed checkstyle issue.
khaykov Jun 13, 2019
389b06a
Merge branch 'develop' of github.com:wordpress-mobile/WordPress-Andro…
khaykov Jun 14, 2019
e3ab2b1
View model refactoring.
khaykov Jun 14, 2019
05a9e3f
Added search availability logic to view model.
khaykov Jun 16, 2019
741021f
Added empty search view test.
khaykov Jun 16, 2019
6347231
Added main view model tests.
khaykov Jun 16, 2019
7610173
Do not recreate search fragment on orientation change.
khaykov Jun 16, 2019
c7b6ec8
Fixed checkstyle issues.
khaykov Jun 16, 2019
4c7d8e2
Removed redundant code, naming changes.
khaykov Jun 16, 2019
5368fde
Fixed search button delay.
khaykov Jun 19, 2019
bfd8aaa
Commiting fragment only when it's null.
khaykov Jun 19, 2019
88847fa
Bringing back error view for post search.
khaykov Jun 20, 2019
dd147ac
Brought back search progress handler.
khaykov Jun 20, 2019
f16b9c3
- Delayed search logic in VM.
khaykov Jun 20, 2019
26b02f6
Force search list to compact layout type.
khaykov Jun 20, 2019
622097f
Added experimental annotation to test.
khaykov Jun 20, 2019
f556c5f
Merge branch 'develop' of github.com:wordpress-mobile/WordPress-Andro…
khaykov Jun 20, 2019
8b4e27c
Fixed test with ThrottleLiveData
khaykov Jun 20, 2019
d15cb67
Simplified empty view logic with increased throttling.
khaykov Jun 20, 2019
d6ade8f
Fixed test, Updated empty view to be top aligned for search list.
khaykov Jun 20, 2019
0853b79
Moved search progress delay logic inside VM.
khaykov Jun 21, 2019
d520eae
Addressed PR feedback.
khaykov Jun 24, 2019
dec570c
Removed unused runnable.
khaykov Jun 24, 2019
9a002e0
Prevent search list view type from resetting on configuration change.
khaykov Jun 24, 2019
00cec08
Prevent search from being executed for the same query.
khaykov Jun 24, 2019
62dbb68
Fixed test.
khaykov Jun 24, 2019
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
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
12.7
-----
* Added Post search
* Show upload in progress notification when retrying
* Fix issue where two identical drafts were created
* Fixed issue where text appeared behind list of themes on the theme screen
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.wordpress.android.ui.posts

import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
Expand All @@ -17,6 +19,7 @@ import org.wordpress.android.R
import org.wordpress.android.WordPress
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.ui.ActionableEmptyView
import org.wordpress.android.ui.posts.PostListType.SEARCH
import org.wordpress.android.ui.posts.PostListViewLayoutType.COMPACT
import org.wordpress.android.ui.posts.PostListViewLayoutType.STANDARD
import org.wordpress.android.ui.posts.adapters.PostListAdapter
Expand All @@ -40,12 +43,15 @@ import javax.inject.Inject
private const val EXTRA_POST_LIST_AUTHOR_FILTER = "post_list_author_filter"
private const val EXTRA_POST_LIST_TYPE = "post_list_type"
private const val MAX_INDEX_FOR_VISIBLE_ITEM_TO_KEEP_SCROLL_POSITION = 2
private const val SEARCH_DELAY_MS = 500L
private const val SEARCH_PROGRESS_INDICATOR_DELAY_MS = 1000L

class PostListFragment : Fragment() {
@Inject internal lateinit var imageManager: ImageManager
@Inject internal lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject internal lateinit var uiHelpers: UiHelpers
private lateinit var viewModel: PostListViewModel
private lateinit var mainViewModel: PostListMainViewModel

private var swipeToRefreshHelper: SwipeToRefreshHelper? = null

Expand All @@ -57,9 +63,13 @@ class PostListFragment : Fragment() {
private lateinit var itemDecorationCompactLayout: RecyclerItemDecoration
private lateinit var itemDecorationStandardLayout: RecyclerItemDecoration

private lateinit var postListType: PostListType

private lateinit var nonNullActivity: FragmentActivity
private lateinit var site: SiteModel

private val searchHandler = Handler()
malinajirka marked this conversation as resolved.
Show resolved Hide resolved

private val postViewHolderConfig: PostViewHolderConfig by lazy {
val displayWidth = DisplayUtils.getDisplayPixelWidth(context)
val contentSpacing = nonNullActivity.resources.getDimensionPixelSize(R.dimen.content_margin)
Expand Down Expand Up @@ -101,13 +111,10 @@ class PostListFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
postListType = requireNotNull(arguments).getSerializable(EXTRA_POST_LIST_TYPE) as PostListType

val authorFilter: AuthorFilterSelection = requireNotNull(arguments)
.getSerializable(EXTRA_POST_LIST_AUTHOR_FILTER) as AuthorFilterSelection
val postListType = requireNotNull(arguments).getSerializable(EXTRA_POST_LIST_TYPE) as PostListType
val mainViewModel = ViewModelProviders.of(nonNullActivity, viewModelFactory)
mainViewModel = ViewModelProviders.of(nonNullActivity, viewModelFactory)
.get(PostListMainViewModel::class.java)

mainViewModel.viewLayoutType.observe(this, Observer { optionaLayoutType ->
optionaLayoutType?.let { layoutType ->
when (layoutType) {
Expand All @@ -125,18 +132,54 @@ class PostListFragment : Fragment() {
postListAdapter.updateItemLayoutType(layoutType)
}
})
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get<PostListViewModel>(PostListViewModel::class.java)
viewModel.start(mainViewModel.getPostListViewModelConnector(authorFilter, postListType))
viewModel.pagedListData.observe(this, Observer {
it?.let { pagedListData -> updatePagedListData(pagedListData) }
})

val authorFilter: AuthorFilterSelection = requireNotNull(arguments)
.getSerializable(EXTRA_POST_LIST_AUTHOR_FILTER) as AuthorFilterSelection
val postListViewModelConnector = mainViewModel.getPostListViewModelConnector(authorFilter, postListType)

viewModel = ViewModelProviders.of(this, viewModelFactory).get<PostListViewModel>(PostListViewModel::class.java)
viewModel.start(postListViewModelConnector)

initObservers()
}

private fun initObservers() {
if (postListType == SEARCH) {
mainViewModel.searchQuery.observe(this, Observer {
if (TextUtils.isEmpty(it)) {
postListAdapter.submitList(null)
}

searchHandler.removeCallbacksAndMessages(null)
searchHandler.postDelayed({
viewModel.search(it)
}, SEARCH_DELAY_MS)
})
}

viewModel.emptyViewState.observe(this, Observer {
it?.let { emptyViewState -> updateEmptyViewForState(emptyViewState) }
})

viewModel.isFetchingFirstPage.observe(this, Observer {
swipeRefreshLayout?.isRefreshing = it == true
if (postListType != SEARCH) {
swipeRefreshLayout?.isRefreshing = it == true
} else {
// most of the time search is pretty fast, so we don't need to show any progress indication
// we delay progress indicator in case request takes longer then expected
if (it == true) {
malinajirka marked this conversation as resolved.
Show resolved Hide resolved
searchHandler.postDelayed(searchProgressRunnable, SEARCH_PROGRESS_INDICATOR_DELAY_MS)
} else {
searchHandler.removeCallbacks(searchProgressRunnable)
swipeRefreshLayout?.isRefreshing = false
}
}
})

viewModel.pagedListData.observe(this, Observer {
it?.let { pagedListData -> updatePagedListData(pagedListData) }
})

viewModel.isLoadingMore.observe(this, Observer {
progressLoadMore?.visibility = if (it == true) View.VISIBLE else View.GONE
})
Expand All @@ -147,6 +190,10 @@ class PostListFragment : Fragment() {
})
}

private val searchProgressRunnable = Runnable {
khaykov marked this conversation as resolved.
Show resolved Hide resolved
swipeRefreshLayout?.isRefreshing = true
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putSerializable(WordPress.SITE, site)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.wordpress.android.R
import org.wordpress.android.analytics.AnalyticsTracker.Stat.POST_LIST_AUTHOR_FILTER_CHANGED
import org.wordpress.android.analytics.AnalyticsTracker.Stat.POST_LIST_SEARCH_ACCESSED
import org.wordpress.android.analytics.AnalyticsTracker.Stat.POST_LIST_TAB_CHANGED
import org.wordpress.android.fluxc.Dispatcher
import org.wordpress.android.fluxc.generated.ListActionBuilder
Expand All @@ -40,6 +41,7 @@ import org.wordpress.android.ui.posts.PostListViewLayoutTypeMenuUiState.Standard
import org.wordpress.android.ui.prefs.AppPrefsWrapper
import org.wordpress.android.ui.uploads.LocalDraftUploadStarter
import org.wordpress.android.util.NetworkUtilsWrapper
import org.wordpress.android.util.SiteUtils
import org.wordpress.android.util.ToastUtils.Duration
import org.wordpress.android.util.analytics.AnalyticsUtils
import org.wordpress.android.viewmodel.SingleLiveEvent
Expand All @@ -53,6 +55,7 @@ import javax.inject.Named
import kotlin.coroutines.CoroutineContext

private const val SCROLL_TO_DELAY = 50L
private const val SEARCH_COLLAPSE_DELAY = 500L
private val FAB_VISIBLE_POST_LIST_PAGES = listOf(PUBLISHED, DRAFTS)
val POST_LIST_PAGES = listOf(PUBLISHED, DRAFTS, SCHEDULED, TRASHED)
private const val TRACKS_SELECTED_TAB = "selected_tab"
Expand Down Expand Up @@ -113,6 +116,15 @@ class PostListMainViewModel @Inject constructor(
private val _viewLayoutTypeMenuUiState = MutableLiveData<PostListViewLayoutTypeMenuUiState>()
val viewLayoutTypeMenuUiState: LiveData<PostListViewLayoutTypeMenuUiState> = _viewLayoutTypeMenuUiState

private val _isSearchExpanded = MutableLiveData<Boolean>()
val isSearchExpanded: LiveData<Boolean> = _isSearchExpanded

private val _isSearchAvailable = MutableLiveData<Boolean>()
val isSearchAvailable: LiveData<Boolean> = _isSearchAvailable

private val _searchQuery = MutableLiveData<String>()
val searchQuery: LiveData<String> = _searchQuery

private val uploadStatusTracker = PostListUploadStatusTracker(uploadStore = uploadStore)
private val featuredImageTracker = PostListFeaturedImageTracker(dispatcher = dispatcher, mediaStore = mediaStore)

Expand Down Expand Up @@ -205,9 +217,11 @@ class PostListMainViewModel @Inject constructor(
}
)

_isSearchAvailable.value = SiteUtils.isAccessedViaWPComRest(site)
_updatePostsPager.value = authorFilterSelection
_viewState.value = PostListMainViewState(
isFabVisible = FAB_VISIBLE_POST_LIST_PAGES.contains(POST_LIST_PAGES.first()),
isFabVisible = FAB_VISIBLE_POST_LIST_PAGES.contains(POST_LIST_PAGES.first()) &&
isSearchExpanded.value != true,
isAuthorFilterVisible = isFilteringByAuthorSupported,
authorFilterSelection = authorFilterSelection,
authorFilterItems = getAuthorFilterItems(authorFilterSelection, accountStore.account?.avatarUrl)
Expand Down Expand Up @@ -239,10 +253,42 @@ class PostListMainViewModel @Inject constructor(
getUploadStatus = uploadStatusTracker::getUploadStatus,
doesPostHaveUnhandledConflict = postConflictResolver::doesPostHaveUnhandledConflict,
postFetcher = postFetcher,
searchQuery = searchQuery.value,
getFeaturedImageUrl = featuredImageTracker::getFeaturedImageUrl
)
}

fun onSearchExpanded(restorePreviousSearch: Boolean) {
if (isSearchExpanded.value != true) {
AnalyticsUtils.trackWithSiteDetails(POST_LIST_SEARCH_ACCESSED, site)

if (!restorePreviousSearch) {
clearSearch()
}

_isSearchExpanded.value = true
_viewState.value = _viewState.value?.copy(isFabVisible = false)
}
}

fun onSearchCollapsed(delay: Long = SEARCH_COLLAPSE_DELAY) {
_isSearchExpanded.value = false
clearSearch()

launch {
delay(delay)
}
_viewState.value = _viewState.value?.copy(isFabVisible = true)
malinajirka marked this conversation as resolved.
Show resolved Hide resolved
}

fun onSearch(searchQuery: String) {
_searchQuery.value = searchQuery
}

private fun clearSearch() {
_searchQuery.value = null
}

fun newPost() {
postActionHandler.newPost()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.wordpress.android.ui.posts.AuthorFilterSelection.ME
import org.wordpress.android.ui.utils.UiString
import org.wordpress.android.ui.utils.UiString.UiStringRes

class PostListMainViewState(
data class PostListMainViewState(
val isFabVisible: Boolean,
val isAuthorFilterVisible: Boolean,
val authorFilterSelection: AuthorFilterSelection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import org.wordpress.android.fluxc.model.post.PostStatus
import org.wordpress.android.fluxc.model.post.PostStatus.PRIVATE

enum class PostListType(val postStatuses: List<PostStatus>) {
PUBLISHED(listOf(PostStatus.PUBLISHED, PostStatus.PRIVATE)),
PUBLISHED(listOf(PostStatus.PUBLISHED, PRIVATE)),
DRAFTS(listOf(PostStatus.DRAFT, PostStatus.PENDING)),
SCHEDULED(listOf(PostStatus.SCHEDULED)),
TRASHED(listOf(PostStatus.TRASHED));
TRASHED(listOf(PostStatus.TRASHED)),
SEARCH(listOf(PostStatus.PUBLISHED, PRIVATE));
malinajirka marked this conversation as resolved.
Show resolved Hide resolved

val titleResId: Int
get() = when (this) {
PUBLISHED -> R.string.post_list_published
DRAFTS -> R.string.post_list_drafts
SCHEDULED -> R.string.post_list_scheduled
TRASHED -> R.string.post_list_trashed
SEARCH -> 0 // we don't have title for search list
}

companion object {
Expand Down
Loading