From 7cb76aad9719b278b096f99c2da3659c3d78e406 Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Mon, 13 Jan 2020 13:57:44 +0100 Subject: [PATCH] Fix search bugs (#1624) * fix toggling media visibility * cleanup search code to make it more readable * remove redundant OnQueryTextListener this is the default behavior * fix bookmarking * fix status interaction causing unnecessary network requests --- .../tusky/components/search/SearchActivity.kt | 16 +--- .../components/search/SearchViewModel.kt | 49 ++++++------ .../search/adapter/SearchAccountsAdapter.kt | 12 +-- .../search/adapter/SearchDataSource.kt | 30 ++++---- .../search/adapter/SearchDataSourceFactory.kt | 8 +- .../search/adapter/SearchHashtagsAdapter.kt | 12 +-- .../search/adapter/SearchRepository.kt | 2 +- .../search/adapter/SearchStatusesAdapter.kt | 4 +- .../search/fragments/SearchFragment.kt | 23 +++--- .../fragments/SearchStatusesFragment.kt | 74 +++++++++---------- 10 files changed, 101 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt index 83c0688a3..6da50940e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchActivity.kt @@ -28,13 +28,12 @@ import com.keylesspalace.tusky.BottomSheetActivity import com.keylesspalace.tusky.R import com.keylesspalace.tusky.components.search.adapter.SearchPagerAdapter import com.keylesspalace.tusky.di.ViewModelFactory -import dagger.android.AndroidInjector import dagger.android.DispatchingAndroidInjector import dagger.android.HasAndroidInjector import kotlinx.android.synthetic.main.activity_search.* import javax.inject.Inject -class SearchActivity : BottomSheetActivity(), SearchView.OnQueryTextListener, HasAndroidInjector { +class SearchActivity : BottomSheetActivity(), HasAndroidInjector { @Inject lateinit var androidInjector: DispatchingAndroidInjector @@ -94,14 +93,6 @@ class SearchActivity : BottomSheetActivity(), SearchView.OnQueryTextListener, Ha return super.onOptionsItemSelected(item) } - override fun onQueryTextChange(newText: String): Boolean { - return false - } - - override fun onQueryTextSubmit(query: String): Boolean { - return false - } - private fun getPageTitle(position: Int): CharSequence? { return when (position) { 0 -> getString(R.string.title_statuses) @@ -123,15 +114,12 @@ class SearchActivity : BottomSheetActivity(), SearchView.OnQueryTextListener, Ha searchView.setSearchableInfo((getSystemService(Context.SEARCH_SERVICE) as? SearchManager)?.getSearchableInfo(componentName)) - searchView.setOnQueryTextListener(this) searchView.requestFocus() searchView.maxWidth = Integer.MAX_VALUE } - override fun androidInjector(): AndroidInjector? { - return androidInjector - } + override fun androidInjector() = androidInjector companion object { @JvmStatic diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt index 9ae7095fa..7979fee55 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt @@ -3,18 +3,15 @@ package com.keylesspalace.tusky.components.search import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Transformations import androidx.paging.PagedList import com.keylesspalace.tusky.components.search.adapter.SearchRepository import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.entity.* +import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.TimelineCases -import com.keylesspalace.tusky.util.Listing -import com.keylesspalace.tusky.util.NetworkState -import com.keylesspalace.tusky.util.RxAwareViewModel -import com.keylesspalace.tusky.util.ViewDataUtils +import com.keylesspalace.tusky.util.* import com.keylesspalace.tusky.viewdata.StatusViewData import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers @@ -23,7 +20,8 @@ import javax.inject.Inject class SearchViewModel @Inject constructor( mastodonApi: MastodonApi, private val timelineCases: TimelineCases, - private val accountManager: AccountManager) : RxAwareViewModel() { + private val accountManager: AccountManager +) : RxAwareViewModel() { var currentQuery: String = "" @@ -33,49 +31,46 @@ class SearchViewModel @Inject constructor( accountManager.activeAccount = value } - val mediaPreviewEnabled: Boolean - get() = activeAccount?.mediaPreviewEnabled ?: false + val mediaPreviewEnabled = activeAccount?.mediaPreviewEnabled ?: false + val alwaysShowSensitiveMedia = activeAccount?.alwaysShowSensitiveMedia ?: false + val alwaysOpenSpoiler = activeAccount?.alwaysOpenSpoiler ?: false private val statusesRepository = SearchRepository>(mastodonApi) private val accountsRepository = SearchRepository(mastodonApi) private val hashtagsRepository = SearchRepository(mastodonApi) - val alwaysShowSensitiveMedia: Boolean = activeAccount?.alwaysShowSensitiveMedia - ?: false - val alwaysOpenSpoiler: Boolean = activeAccount?.alwaysOpenSpoiler - ?: false private val repoResultStatus = MutableLiveData>>() - val statuses: LiveData>> = Transformations.switchMap(repoResultStatus) { it.pagedList } - val networkStateStatus: LiveData = Transformations.switchMap(repoResultStatus) { it.networkState } - val networkStateStatusRefresh: LiveData = Transformations.switchMap(repoResultStatus) { it.refreshState } + val statuses: LiveData>> = repoResultStatus.switchMap { it.pagedList } + val networkStateStatus: LiveData = repoResultStatus.switchMap { it.networkState } + val networkStateStatusRefresh: LiveData = repoResultStatus.switchMap { it.refreshState } private val repoResultAccount = MutableLiveData>() - val accounts: LiveData> = Transformations.switchMap(repoResultAccount) { it.pagedList } - val networkStateAccount: LiveData = Transformations.switchMap(repoResultAccount) { it.networkState } - val networkStateAccountRefresh: LiveData = Transformations.switchMap(repoResultAccount) { it.refreshState } + val accounts: LiveData> = repoResultAccount.switchMap { it.pagedList } + val networkStateAccount: LiveData = repoResultAccount.switchMap { it.networkState } + val networkStateAccountRefresh: LiveData = repoResultAccount.switchMap { it.refreshState } private val repoResultHashTag = MutableLiveData>() - val hashtags: LiveData> = Transformations.switchMap(repoResultHashTag) { it.pagedList } - val networkStateHashTag: LiveData = Transformations.switchMap(repoResultHashTag) { it.networkState } - val networkStateHashTagRefresh: LiveData = Transformations.switchMap(repoResultHashTag) { it.refreshState } + val hashtags: LiveData> = repoResultHashTag.switchMap { it.pagedList } + val networkStateHashTag: LiveData = repoResultHashTag.switchMap { it.networkState } + val networkStateHashTagRefresh: LiveData = repoResultHashTag.switchMap { it.refreshState } private val loadedStatuses = ArrayList>() fun search(query: String) { loadedStatuses.clear() repoResultStatus.value = statusesRepository.getSearchData(SearchType.Status, query, disposables, initialItems = loadedStatuses) { - (it?.statuses?.map { status -> Pair(status, ViewDataUtils.statusToViewData(status, alwaysShowSensitiveMedia, alwaysOpenSpoiler)!!) } - ?: emptyList()) + it?.statuses?.map { status -> Pair(status, ViewDataUtils.statusToViewData(status, alwaysShowSensitiveMedia, alwaysOpenSpoiler)!!) } + .orEmpty() .apply { loadedStatuses.addAll(this) } } repoResultAccount.value = accountsRepository.getSearchData(SearchType.Account, query, disposables) { - it?.accounts ?: emptyList() + it?.accounts.orEmpty() } val hashtagQuery = if (query.startsWith("#")) query else "#$query" repoResultHashTag.value = hashtagsRepository.getSearchData(SearchType.Hashtag, hashtagQuery, disposables) { - it?.hashtags ?: emptyList() + it?.hashtags.orEmpty() } } @@ -184,11 +179,11 @@ class SearchViewModel @Inject constructor( fun bookmark(status: Pair, isBookmarked: Boolean) { val idx = loadedStatuses.indexOf(status) if (idx >= 0) { - val newPair = Pair(status.first, StatusViewData.Builder(status.second).setFavourited(isBookmarked).createStatusViewData()) + val newPair = Pair(status.first, StatusViewData.Builder(status.second).setBookmarked(isBookmarked).createStatusViewData()) loadedStatuses[idx] = newPair repoResultStatus.value?.refresh?.invoke() } - timelineCases.favourite(status.first, isBookmarked) + timelineCases.bookmark(status.first, isBookmarked) .onErrorReturnItem(status.first) .subscribe() .autoDispose() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchAccountsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchAccountsAdapter.kt index 918e7a427..c135ad7d6 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchAccountsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchAccountsAdapter.kt @@ -26,8 +26,7 @@ import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.interfaces.LinkListener class SearchAccountsAdapter(private val linkListener: LinkListener) - : PagedListAdapter(STATUS_COMPARATOR) { - + : PagedListAdapter(ACCOUNT_COMPARATOR) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val view = LayoutInflater.from(parent.context) @@ -37,21 +36,16 @@ class SearchAccountsAdapter(private val linkListener: LinkListener) override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { getItem(position)?.let { item -> - (holder as? AccountViewHolder)?.apply { + (holder as AccountViewHolder).apply { setupWithAccount(item) setupLinkListener(linkListener) } } - - } - - public override fun getItem(position: Int): Account? { - return super.getItem(position) } companion object { - val STATUS_COMPARATOR = object : DiffUtil.ItemCallback() { + val ACCOUNT_COMPARATOR = object : DiffUtil.ItemCallback() { override fun areContentsTheSame(oldItem: Account, newItem: Account): Boolean = oldItem.deepEquals(newItem) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchDataSource.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchDataSource.kt index d3da37484..f02038dbd 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchDataSource.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchDataSource.kt @@ -15,7 +15,6 @@ package com.keylesspalace.tusky.components.search.adapter -import android.annotation.SuppressLint import androidx.lifecycle.MutableLiveData import androidx.paging.PositionalDataSource import com.keylesspalace.tusky.components.search.SearchType @@ -23,16 +22,18 @@ import com.keylesspalace.tusky.entity.SearchResult import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.NetworkState import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.addTo import java.util.concurrent.Executor class SearchDataSource( private val mastodonApi: MastodonApi, private val searchType: SearchType, - private val searchRequest: String?, + private val searchRequest: String, private val disposables: CompositeDisposable, private val retryExecutor: Executor, private val initialItems: List? = null, - private val parser: (SearchResult?) -> List) : PositionalDataSource() { + private val parser: (SearchResult?) -> List, + private val source: SearchDataSourceFactory) : PositionalDataSource() { val networkState = MutableLiveData() @@ -48,24 +49,20 @@ class SearchDataSource( } } - @SuppressLint("CheckResult") override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) { if (!initialItems.isNullOrEmpty()) { - callback.onResult(initialItems, 0) + callback.onResult(initialItems.toList(), 0) } else { networkState.postValue(NetworkState.LOADED) retry = null initialLoad.postValue(NetworkState.LOADING) mastodonApi.searchObservable( - query = searchRequest ?: "", + query = searchRequest, type = searchType.apiParameter, resolve = true, limit = params.requestedLoadSize, offset = 0, following =false) - .doOnSubscribe { - disposables.add(it) - } .subscribe( { data -> val res = parser(data) @@ -79,19 +76,18 @@ class SearchDataSource( } initialLoad.postValue(NetworkState.error(error.message)) } - ) + ).addTo(disposables) } } - @SuppressLint("CheckResult") override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback) { networkState.postValue(NetworkState.LOADING) retry = null + if(source.exhausted) { + return callback.onResult(emptyList()) + } mastodonApi.searchObservable(searchType.apiParameter, searchRequest, true, params.loadSize, params.startPosition, false) - .doOnSubscribe { - disposables.add(it) - } .subscribe( { data -> // Working around Mastodon bug where exact match is returned no matter @@ -105,9 +101,11 @@ class SearchDataSource( } else { parser(data) } + if(res.isEmpty()) { + source.exhausted = true + } callback.onResult(res) networkState.postValue(NetworkState.LOADED) - }, { error -> retry = { @@ -115,7 +113,7 @@ class SearchDataSource( } networkState.postValue(NetworkState.error(error.message)) } - ) + ).addTo(disposables) } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchDataSourceFactory.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchDataSourceFactory.kt index 7877c778b..b47da7017 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchDataSourceFactory.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchDataSourceFactory.kt @@ -26,14 +26,18 @@ import java.util.concurrent.Executor class SearchDataSourceFactory( private val mastodonApi: MastodonApi, private val searchType: SearchType, - private val searchRequest: String?, + private val searchRequest: String, private val disposables: CompositeDisposable, private val retryExecutor: Executor, private val cacheData: List? = null, private val parser: (SearchResult?) -> List) : DataSource.Factory() { + val sourceLiveData = MutableLiveData>() + + var exhausted = false + override fun create(): DataSource { - val source = SearchDataSource(mastodonApi, searchType, searchRequest, disposables, retryExecutor, cacheData, parser) + val source = SearchDataSource(mastodonApi, searchType, searchRequest, disposables, retryExecutor, cacheData, parser, this) sourceLiveData.postValue(source) return source } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt index 7f172a379..71863d430 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchHashtagsAdapter.kt @@ -26,8 +26,7 @@ import com.keylesspalace.tusky.entity.HashTag import com.keylesspalace.tusky.interfaces.LinkListener class SearchHashtagsAdapter(private val linkListener: LinkListener) - : PagedListAdapter(STATUS_COMPARATOR) { - + : PagedListAdapter(HASHTAG_COMPARATOR) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val view = LayoutInflater.from(parent.context) @@ -36,17 +35,14 @@ class SearchHashtagsAdapter(private val linkListener: LinkListener) } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - getItem(position)?.let { item -> - (holder as? HashtagViewHolder)?.apply { - setup(item.name, linkListener) - } + getItem(position)?.let { (name) -> + (holder as HashtagViewHolder).setup(name, linkListener) } - } companion object { - val STATUS_COMPARATOR = object : DiffUtil.ItemCallback() { + val HASHTAG_COMPARATOR = object : DiffUtil.ItemCallback() { override fun areContentsTheSame(oldItem: HashTag, newItem: HashTag): Boolean = oldItem.name == newItem.name diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchRepository.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchRepository.kt index 2cc12c20f..28d9564dc 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchRepository.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchRepository.kt @@ -29,7 +29,7 @@ class SearchRepository(private val mastodonApi: MastodonApi) { private val executor = Executors.newSingleThreadExecutor() - fun getSearchData(searchType: SearchType, searchRequest: String?, disposables: CompositeDisposable, pageSize: Int = 20, + fun getSearchData(searchType: SearchType, searchRequest: String, disposables: CompositeDisposable, pageSize: Int = 20, initialItems: List? = null, parser: (SearchResult?) -> List): Listing { val sourceFactory = SearchDataSourceFactory(mastodonApi, searchType, searchRequest, disposables, executor, initialItems, parser) val livePagedList = sourceFactory.toLiveData( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchStatusesAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchStatusesAdapter.kt index aaeb32ef6..0fcee37d1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchStatusesAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/adapter/SearchStatusesAdapter.kt @@ -32,7 +32,6 @@ class SearchStatusesAdapter( private val statusListener: StatusActionListener ) : PagedListAdapter, RecyclerView.ViewHolder>(STATUS_COMPARATOR) { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_status, parent, false) @@ -41,8 +40,7 @@ class SearchStatusesAdapter( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { getItem(position)?.let { item -> - (holder as? StatusViewHolder)?.setupWithStatus(item.second, statusListener, - statusDisplayOptions) + (holder as StatusViewHolder).setupWithStatus(item.second, statusListener, statusDisplayOptions) } } diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt index 102547e94..b4a36e2ad 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchFragment.kt @@ -12,6 +12,7 @@ import androidx.paging.PagedList import androidx.paging.PagedListAdapter import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.SimpleItemAnimator import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.AccountActivity @@ -62,8 +63,8 @@ abstract class SearchFragment : Fragment(), swipeRefreshLayout.setOnRefreshListener(this) swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue) swipeRefreshLayout.setProgressBackgroundColorSchemeColor( - ThemeUtils.getColor(swipeRefreshLayout.context, android.R.attr.colorBackground)) - + ThemeUtils.getColor(swipeRefreshLayout.context, android.R.attr.colorBackground) + ) } private fun subscribeObservables() { @@ -75,8 +76,9 @@ abstract class SearchFragment : Fragment(), searchProgressBar.visible(it == NetworkState.LOADING) - if (it.status == Status.FAILED) - showError(it.msg) + if (it.status == Status.FAILED) { + showError() + } checkNoData() }) @@ -85,8 +87,9 @@ abstract class SearchFragment : Fragment(), progressBarBottom.visible(it == NetworkState.LOADING) - if (it.status == Status.FAILED) - showError(it.msg) + if (it.status == Status.FAILED) { + showError() + } }) } @@ -99,7 +102,8 @@ abstract class SearchFragment : Fragment(), searchRecyclerView.layoutManager = LinearLayoutManager(searchRecyclerView.context) adapter = createAdapter() searchRecyclerView.adapter = adapter - + searchRecyclerView.setHasFixedSize(true) + (searchRecyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false } private fun showNoData(isEmpty: Boolean) { @@ -109,7 +113,7 @@ abstract class SearchFragment : Fragment(), searchNoResultsText.hide() } - private fun showError(@Suppress("UNUSED_PARAMETER") msg: String?) { + private fun showError() { if (snackbarErrorRetry?.isShown != true) { snackbarErrorRetry = Snackbar.make(layoutRoot, R.string.failed_search, Snackbar.LENGTH_INDEFINITE) snackbarErrorRetry?.setAction(R.string.action_retry) { @@ -129,13 +133,12 @@ abstract class SearchFragment : Fragment(), } protected val bottomSheetActivity - get() = (activity as? BottomSheetActivity) + get() = (activity as? BottomSheetActivity) override fun onRefresh() { // Dismissed here because the RecyclerView bottomProgressBar is shown as soon as the retry begins. swipeRefreshLayout.post { - swipeRefreshLayout.isRefreshing = false } viewModel.retryAllSearches() diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt index 8df8e6de4..c384d123c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/fragments/SearchStatusesFragment.kt @@ -59,7 +59,6 @@ import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from import com.uber.autodispose.autoDispose import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.android.synthetic.main.fragment_search.* -import java.util.* class SearchStatusesFragment : SearchFragment>(), StatusActionListener { @@ -70,6 +69,9 @@ class SearchStatusesFragment : SearchFragment>> get() = viewModel.statuses + private val searchAdapter + get() = super.adapter as SearchStatusesAdapter + override fun createAdapter(): PagedListAdapter, *> { val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context) val statusDisplayOptions = StatusDisplayOptions( @@ -87,37 +89,37 @@ class SearchStatusesFragment : SearchFragment + searchAdapter.getItem(position)?.first?.let { status -> reply(status) } } override fun onFavourite(favourite: Boolean, position: Int) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.let { status -> + searchAdapter.getItem(position)?.let { status -> viewModel.favorite(status, favourite) } } override fun onBookmark(bookmark: Boolean, position: Int) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.let { status -> + searchAdapter.getItem(position)?.let { status -> viewModel.bookmark(status, bookmark) } } override fun onMore(view: View, position: Int) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let { + searchAdapter.getItem(position)?.first?.let { more(it, view, position) } } override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.actionableStatus?.let { actionable -> + searchAdapter.getItem(position)?.first?.actionableStatus?.let { actionable -> when (actionable.attachments[attachmentIndex].type) { Attachment.Type.GIFV, Attachment.Type.VIDEO, Attachment.Type.IMAGE, Attachment.Type.AUDIO -> { val attachments = AttachmentViewData.list(actionable) @@ -142,48 +144,48 @@ class SearchStatusesFragment : SearchFragment + searchAdapter.getItem(position)?.first?.let { status -> val actionableStatus = status.actionableStatus bottomSheetActivity?.viewThread(actionableStatus.id, actionableStatus.url) } } override fun onOpenReblog(position: Int) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let { status -> + searchAdapter.getItem(position)?.first?.let { status -> bottomSheetActivity?.viewAccount(status.account.id) } } override fun onExpandedChange(expanded: Boolean, position: Int) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.let { + searchAdapter.getItem(position)?.let { viewModel.expandedChange(it, expanded) } } override fun onLoadMore(position: Int) { - //Ignore + // Not possible here } override fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.let { + searchAdapter.getItem(position)?.let { viewModel.collapsedChange(it, isCollapsed) } } override fun onVoteInPoll(position: Int, choices: MutableList) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.let { + searchAdapter.getItem(position)?.let { viewModel.voteInPoll(it, choices) } } private fun removeItem(position: Int) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.let { + searchAdapter.getItem(position)?.let { viewModel.removeItem(it) } } override fun onReblog(reblog: Boolean, position: Int) { - (adapter as? SearchStatusesAdapter)?.getItem(position)?.let { status -> + searchAdapter.getItem(position)?.let { status -> viewModel.reblog(status, reblog) } } @@ -193,27 +195,23 @@ class SearchStatusesFragment : SearchFragment() - mentionedUsernames.add(actionableStatus.account.username) - val loggedInUsername = viewModel.activeAccount?.username - for ((_, _, username) in mentions) { - mentionedUsernames.add(username) - } - mentionedUsernames.remove(loggedInUsername) - val intent = ComposeActivity.startIntent(context!!, ComposeOptions( - inReplyToId = inReplyToId, - replyVisibility = replyVisibility, - contentWarning = contentWarning, + val mentionedUsernames = actionableStatus.mentions.map { it.username } + .toMutableSet() + .apply { + add(actionableStatus.account.username) + remove(viewModel.activeAccount?.username) + } + + val intent = ComposeActivity.startIntent(requireContext(), ComposeOptions( + inReplyToId = status.actionableId, + replyVisibility = actionableStatus.visibility, + contentWarning = actionableStatus.spoilerText, mentionedUsernames = mentionedUsernames, replyingStatusAuthor = actionableStatus.account.localUsername, replyingStatusContent = actionableStatus.content.toString() )) - requireActivity().startActivity(intent) + startActivity(intent) } private fun more(status: Status, view: View, position: Int) { @@ -252,8 +250,7 @@ class SearchStatusesFragment : SearchFragment openAsItem.isVisible = false 2 -> for (account in accounts) { @@ -269,13 +266,12 @@ class SearchStatusesFragment : SearchFragment when (item.itemId) { R.id.status_share_content -> { - var statusToShare: Status? = status - if (statusToShare!!.reblog != null) statusToShare = statusToShare.reblog + val statusToShare: Status = status.actionableStatus val sendIntent = Intent() sendIntent.action = Intent.ACTION_SEND - val stringToShare = statusToShare!!.account.username + + val stringToShare = statusToShare.account.username + " - " + statusToShare.content.toString() sendIntent.putExtra(Intent.EXTRA_TEXT, stringToShare) @@ -292,7 +288,7 @@ class SearchStatusesFragment : SearchFragment { - val clipboard = activity!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clipboard = requireActivity().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager clipboard.setPrimaryClip(ClipData.newPlainText(null, statusUrl)) return@setOnMenuItemClickListener true } @@ -365,7 +361,7 @@ class SearchStatusesFragment : SearchFragment