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
This commit is contained in:
Konrad Pozniak 2020-01-13 13:57:44 +01:00 committed by GitHub
parent f8c7bedfa6
commit 7cb76aad97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 101 additions and 129 deletions

View File

@ -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<Any>
@ -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<Any>? {
return androidInjector
}
override fun androidInjector() = androidInjector
companion object {
@JvmStatic

View File

@ -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<Pair<Status, StatusViewData.Concrete>>(mastodonApi)
private val accountsRepository = SearchRepository<Account>(mastodonApi)
private val hashtagsRepository = SearchRepository<HashTag>(mastodonApi)
val alwaysShowSensitiveMedia: Boolean = activeAccount?.alwaysShowSensitiveMedia
?: false
val alwaysOpenSpoiler: Boolean = activeAccount?.alwaysOpenSpoiler
?: false
private val repoResultStatus = MutableLiveData<Listing<Pair<Status, StatusViewData.Concrete>>>()
val statuses: LiveData<PagedList<Pair<Status, StatusViewData.Concrete>>> = Transformations.switchMap(repoResultStatus) { it.pagedList }
val networkStateStatus: LiveData<NetworkState> = Transformations.switchMap(repoResultStatus) { it.networkState }
val networkStateStatusRefresh: LiveData<NetworkState> = Transformations.switchMap(repoResultStatus) { it.refreshState }
val statuses: LiveData<PagedList<Pair<Status, StatusViewData.Concrete>>> = repoResultStatus.switchMap { it.pagedList }
val networkStateStatus: LiveData<NetworkState> = repoResultStatus.switchMap { it.networkState }
val networkStateStatusRefresh: LiveData<NetworkState> = repoResultStatus.switchMap { it.refreshState }
private val repoResultAccount = MutableLiveData<Listing<Account>>()
val accounts: LiveData<PagedList<Account>> = Transformations.switchMap(repoResultAccount) { it.pagedList }
val networkStateAccount: LiveData<NetworkState> = Transformations.switchMap(repoResultAccount) { it.networkState }
val networkStateAccountRefresh: LiveData<NetworkState> = Transformations.switchMap(repoResultAccount) { it.refreshState }
val accounts: LiveData<PagedList<Account>> = repoResultAccount.switchMap { it.pagedList }
val networkStateAccount: LiveData<NetworkState> = repoResultAccount.switchMap { it.networkState }
val networkStateAccountRefresh: LiveData<NetworkState> = repoResultAccount.switchMap { it.refreshState }
private val repoResultHashTag = MutableLiveData<Listing<HashTag>>()
val hashtags: LiveData<PagedList<HashTag>> = Transformations.switchMap(repoResultHashTag) { it.pagedList }
val networkStateHashTag: LiveData<NetworkState> = Transformations.switchMap(repoResultHashTag) { it.networkState }
val networkStateHashTagRefresh: LiveData<NetworkState> = Transformations.switchMap(repoResultHashTag) { it.refreshState }
val hashtags: LiveData<PagedList<HashTag>> = repoResultHashTag.switchMap { it.pagedList }
val networkStateHashTag: LiveData<NetworkState> = repoResultHashTag.switchMap { it.networkState }
val networkStateHashTagRefresh: LiveData<NetworkState> = repoResultHashTag.switchMap { it.refreshState }
private val loadedStatuses = ArrayList<Pair<Status, StatusViewData.Concrete>>()
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<Status, StatusViewData.Concrete>, 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()

View File

@ -26,8 +26,7 @@ import com.keylesspalace.tusky.entity.Account
import com.keylesspalace.tusky.interfaces.LinkListener
class SearchAccountsAdapter(private val linkListener: LinkListener)
: PagedListAdapter<Account, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
: PagedListAdapter<Account, RecyclerView.ViewHolder>(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<Account>() {
val ACCOUNT_COMPARATOR = object : DiffUtil.ItemCallback<Account>() {
override fun areContentsTheSame(oldItem: Account, newItem: Account): Boolean =
oldItem.deepEquals(newItem)

View File

@ -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<T>(
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<T>? = null,
private val parser: (SearchResult?) -> List<T>) : PositionalDataSource<T>() {
private val parser: (SearchResult?) -> List<T>,
private val source: SearchDataSourceFactory<T>) : PositionalDataSource<T>() {
val networkState = MutableLiveData<NetworkState>()
@ -48,24 +49,20 @@ class SearchDataSource<T>(
}
}
@SuppressLint("CheckResult")
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
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<T>(
}
initialLoad.postValue(NetworkState.error(error.message))
}
)
).addTo(disposables)
}
}
@SuppressLint("CheckResult")
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) {
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<T>(
} else {
parser(data)
}
if(res.isEmpty()) {
source.exhausted = true
}
callback.onResult(res)
networkState.postValue(NetworkState.LOADED)
},
{ error ->
retry = {
@ -115,7 +113,7 @@ class SearchDataSource<T>(
}
networkState.postValue(NetworkState.error(error.message))
}
)
).addTo(disposables)
}

View File

@ -26,14 +26,18 @@ import java.util.concurrent.Executor
class SearchDataSourceFactory<T>(
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<T>? = null,
private val parser: (SearchResult?) -> List<T>) : DataSource.Factory<Int, T>() {
val sourceLiveData = MutableLiveData<SearchDataSource<T>>()
var exhausted = false
override fun create(): DataSource<Int, T> {
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
}

View File

@ -26,8 +26,7 @@ import com.keylesspalace.tusky.entity.HashTag
import com.keylesspalace.tusky.interfaces.LinkListener
class SearchHashtagsAdapter(private val linkListener: LinkListener)
: PagedListAdapter<HashTag, RecyclerView.ViewHolder>(STATUS_COMPARATOR) {
: PagedListAdapter<HashTag, RecyclerView.ViewHolder>(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<HashTag>() {
val HASHTAG_COMPARATOR = object : DiffUtil.ItemCallback<HashTag>() {
override fun areContentsTheSame(oldItem: HashTag, newItem: HashTag): Boolean =
oldItem.name == newItem.name

View File

@ -29,7 +29,7 @@ class SearchRepository<T>(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<T>? = null, parser: (SearchResult?) -> List<T>): Listing<T> {
val sourceFactory = SearchDataSourceFactory(mastodonApi, searchType, searchRequest, disposables, executor, initialItems, parser)
val livePagedList = sourceFactory.toLiveData(

View File

@ -32,7 +32,6 @@ class SearchStatusesAdapter(
private val statusListener: StatusActionListener
) : PagedListAdapter<Pair<Status, StatusViewData.Concrete>, 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)
}
}

View File

@ -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<T> : 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<T> : 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<T> : 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<T> : 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<T> : 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<T> : 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()

View File

@ -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<Pair<Status, StatusViewData.Concrete>>(), StatusActionListener {
@ -70,6 +69,9 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
override val data: LiveData<PagedList<Pair<Status, StatusViewData.Concrete>>>
get() = viewModel.statuses
private val searchAdapter
get() = super.adapter as SearchStatusesAdapter
override fun createAdapter(): PagedListAdapter<Pair<Status, StatusViewData.Concrete>, *> {
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
val statusDisplayOptions = StatusDisplayOptions(
@ -87,37 +89,37 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
override fun onContentHiddenChange(isShowing: Boolean, position: Int) {
(adapter as? SearchStatusesAdapter)?.getItem(position)?.let {
searchAdapter.getItem(position)?.let {
viewModel.contentHiddenChange(it, isShowing)
}
}
override fun onReply(position: Int) {
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let { status ->
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<Pair<Status, StatusViewData.Concre
}
override fun onViewThread(position: Int) {
(adapter as? SearchStatusesAdapter)?.getItem(position)?.first?.let { status ->
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<Int>) {
(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<Pair<Status, StatusViewData.Concre
}
private fun reply(status: Status) {
val inReplyToId = status.actionableId
val actionableStatus = status.actionableStatus
val replyVisibility = actionableStatus.visibility
val contentWarning = actionableStatus.spoilerText
val mentions = actionableStatus.mentions
val mentionedUsernames = LinkedHashSet<String>()
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<Pair<Status, StatusViewData.Concre
}
}
val menu = popup.menu
val openAsItem = menu.findItem(R.id.status_open_as)
val openAsItem = popup.menu.findItem(R.id.status_open_as)
when (accounts.size) {
0, 1 -> openAsItem.isVisible = false
2 -> for (account in accounts) {
@ -269,13 +266,12 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
popup.setOnMenuItemClickListener { item ->
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<Pair<Status, StatusViewData.Concre
return@setOnMenuItemClickListener true
}
R.id.status_copy_link -> {
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<Pair<Status, StatusViewData.Concre
val uri = Uri.parse(url)
val filename = uri.lastPathSegment
val downloadManager = activity!!.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadManager = requireActivity().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val request = DownloadManager.Request(uri)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename)
downloadManager.enqueue(request)
@ -417,7 +413,7 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
deletedStatus
}
val intent = ComposeActivity.startIntent(context!!, ComposeOptions(
val intent = ComposeActivity.startIntent(requireContext(), ComposeOptions(
tootText = redraftStatus.text ?: "",
inReplyToId = redraftStatus.inReplyToId,
visibility = redraftStatus.visibility,