use ViewData only instead of Pair<Status, ViewData> in Search (#2336)

This commit is contained in:
Konrad Pozniak 2022-02-25 18:57:49 +01:00 committed by GitHub
parent c592dfef78
commit 2960a85ff1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 80 deletions

View file

@ -53,17 +53,15 @@ class SearchViewModel @Inject constructor(
val alwaysShowSensitiveMedia = activeAccount?.alwaysShowSensitiveMedia ?: false
val alwaysOpenSpoiler = activeAccount?.alwaysOpenSpoiler ?: false
private val loadedStatuses: MutableList<Pair<Status, StatusViewData.Concrete>> = mutableListOf()
private val loadedStatuses: MutableList<StatusViewData.Concrete> = mutableListOf()
private val statusesPagingSourceFactory = SearchPagingSourceFactory(mastodonApi, SearchType.Status, loadedStatuses) {
it.statuses.map { status ->
val statusViewData = status.toViewData(
status.toViewData(
isShowingContent = alwaysShowSensitiveMedia || !status.actionableStatus.sensitive,
isExpanded = alwaysOpenSpoiler,
isCollapsed = true
)
Pair(status, statusViewData)
}.apply {
loadedStatuses.addAll(this)
}
@ -100,11 +98,11 @@ class SearchViewModel @Inject constructor(
hashtagsPagingSourceFactory.newSearch(query)
}
fun removeItem(status: Pair<Status, StatusViewData.Concrete>) {
timelineCases.delete(status.first.id)
fun removeItem(statusViewData: StatusViewData.Concrete) {
timelineCases.delete(statusViewData.id)
.subscribe(
{
if (loadedStatuses.remove(status))
if (loadedStatuses.remove(statusViewData))
statusesPagingSourceFactory.invalidate()
},
{ err ->
@ -114,82 +112,81 @@ class SearchViewModel @Inject constructor(
.autoDispose()
}
fun expandedChange(status: Pair<Status, StatusViewData.Concrete>, expanded: Boolean) {
val idx = loadedStatuses.indexOf(status)
fun expandedChange(statusViewData: StatusViewData.Concrete, expanded: Boolean) {
val idx = loadedStatuses.indexOf(statusViewData)
if (idx >= 0) {
loadedStatuses[idx] = Pair(status.first, status.second.copy(isExpanded = expanded))
loadedStatuses[idx] = statusViewData.copy(isExpanded = expanded)
statusesPagingSourceFactory.invalidate()
}
}
fun reblog(status: Pair<Status, StatusViewData.Concrete>, reblog: Boolean) {
timelineCases.reblog(status.first.id, reblog)
fun reblog(statusViewData: StatusViewData.Concrete, reblog: Boolean) {
timelineCases.reblog(statusViewData.id, reblog)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ setRebloggedForStatus(status, reblog) },
{ t -> Log.d(TAG, "Failed to reblog status ${status.first.id}", t) }
{ setRebloggedForStatus(statusViewData, reblog) },
{ t -> Log.d(TAG, "Failed to reblog status ${statusViewData.id}", t) }
)
.autoDispose()
}
private fun setRebloggedForStatus(status: Pair<Status, StatusViewData.Concrete>, reblog: Boolean) {
status.first.reblogged = reblog
status.first.reblog?.reblogged = reblog
private fun setRebloggedForStatus(statusViewData: StatusViewData.Concrete, reblog: Boolean) {
statusViewData.status.reblogged = reblog
statusViewData.status.reblog?.reblogged = reblog
statusesPagingSourceFactory.invalidate()
}
fun contentHiddenChange(status: Pair<Status, StatusViewData.Concrete>, isShowing: Boolean) {
val idx = loadedStatuses.indexOf(status)
fun contentHiddenChange(statusViewData: StatusViewData.Concrete, isShowing: Boolean) {
val idx = loadedStatuses.indexOf(statusViewData)
if (idx >= 0) {
loadedStatuses[idx] = Pair(status.first, status.second.copy(isShowingContent = isShowing))
loadedStatuses[idx] = statusViewData.copy(isShowingContent = isShowing)
statusesPagingSourceFactory.invalidate()
}
}
fun collapsedChange(status: Pair<Status, StatusViewData.Concrete>, collapsed: Boolean) {
val idx = loadedStatuses.indexOf(status)
fun collapsedChange(statusViewData: StatusViewData.Concrete, collapsed: Boolean) {
val idx = loadedStatuses.indexOf(statusViewData)
if (idx >= 0) {
loadedStatuses[idx] = Pair(status.first, status.second.copy(isCollapsed = collapsed))
loadedStatuses[idx] = statusViewData.copy(isCollapsed = collapsed)
statusesPagingSourceFactory.invalidate()
}
}
fun voteInPoll(status: Pair<Status, StatusViewData.Concrete>, choices: MutableList<Int>) {
val votedPoll = status.first.actionableStatus.poll!!.votedCopy(choices)
updateStatus(status, votedPoll)
timelineCases.voteInPoll(status.first.id, votedPoll.id, choices)
fun voteInPoll(statusViewData: StatusViewData.Concrete, choices: MutableList<Int>) {
val votedPoll = statusViewData.status.actionableStatus.poll!!.votedCopy(choices)
updateStatus(statusViewData, votedPoll)
timelineCases.voteInPoll(statusViewData.id, votedPoll.id, choices)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ newPoll -> updateStatus(status, newPoll) },
{ t -> Log.d(TAG, "Failed to vote in poll: ${status.first.id}", t) }
{ newPoll -> updateStatus(statusViewData, newPoll) },
{ t -> Log.d(TAG, "Failed to vote in poll: ${statusViewData.id}", t) }
)
.autoDispose()
}
private fun updateStatus(status: Pair<Status, StatusViewData.Concrete>, newPoll: Poll) {
val idx = loadedStatuses.indexOf(status)
private fun updateStatus(statusViewData: StatusViewData.Concrete, newPoll: Poll) {
val idx = loadedStatuses.indexOf(statusViewData)
if (idx >= 0) {
val newStatus = status.first.copy(poll = newPoll)
val newViewData = status.second.copy(status = newStatus)
loadedStatuses[idx] = Pair(newStatus, newViewData)
val newStatus = statusViewData.status.copy(poll = newPoll)
loadedStatuses[idx] = statusViewData.copy(status = newStatus)
statusesPagingSourceFactory.invalidate()
}
}
fun favorite(status: Pair<Status, StatusViewData.Concrete>, isFavorited: Boolean) {
status.first.favourited = isFavorited
fun favorite(statusViewData: StatusViewData.Concrete, isFavorited: Boolean) {
statusViewData.status.favourited = isFavorited
statusesPagingSourceFactory.invalidate()
timelineCases.favourite(status.first.id, isFavorited)
.onErrorReturnItem(status.first)
timelineCases.favourite(statusViewData.id, isFavorited)
.onErrorReturnItem(statusViewData.status)
.subscribe()
.autoDispose()
}
fun bookmark(status: Pair<Status, StatusViewData.Concrete>, isBookmarked: Boolean) {
status.first.bookmarked = isBookmarked
fun bookmark(statusViewData: StatusViewData.Concrete, isBookmarked: Boolean) {
statusViewData.status.bookmarked = isBookmarked
statusesPagingSourceFactory.invalidate()
timelineCases.bookmark(status.first.id, isBookmarked)
.onErrorReturnItem(status.first)
timelineCases.bookmark(statusViewData.id, isBookmarked)
.onErrorReturnItem(statusViewData.status)
.subscribe()
.autoDispose()
}
@ -214,19 +211,15 @@ class SearchViewModel @Inject constructor(
return timelineCases.delete(id)
}
fun muteConversation(status: Pair<Status, StatusViewData.Concrete>, mute: Boolean) {
val idx = loadedStatuses.indexOf(status)
fun muteConversation(statusViewData: StatusViewData.Concrete, mute: Boolean) {
val idx = loadedStatuses.indexOf(statusViewData)
if (idx >= 0) {
val newStatus = status.first.copy(muted = mute)
val newPair = Pair(
newStatus,
status.second.copy(status = newStatus)
)
loadedStatuses[idx] = newPair
val newStatus = statusViewData.status.copy(muted = mute)
loadedStatuses[idx] = statusViewData.copy(status = newStatus)
statusesPagingSourceFactory.invalidate()
}
timelineCases.muteConversation(status.first.id, mute)
.onErrorReturnItem(status.first)
timelineCases.muteConversation(statusViewData.id, mute)
.onErrorReturnItem(statusViewData.status)
.subscribe()
.autoDispose()
}

View file

@ -21,7 +21,6 @@ import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.adapter.StatusViewHolder
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.interfaces.StatusActionListener
import com.keylesspalace.tusky.util.StatusDisplayOptions
import com.keylesspalace.tusky.viewdata.StatusViewData
@ -29,7 +28,7 @@ import com.keylesspalace.tusky.viewdata.StatusViewData
class SearchStatusesAdapter(
private val statusDisplayOptions: StatusDisplayOptions,
private val statusListener: StatusActionListener
) : PagingDataAdapter<Pair<Status, StatusViewData.Concrete>, StatusViewHolder>(STATUS_COMPARATOR) {
) : PagingDataAdapter<StatusViewData.Concrete, StatusViewHolder>(STATUS_COMPARATOR) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatusViewHolder {
val view = LayoutInflater.from(parent.context)
@ -39,22 +38,18 @@ class SearchStatusesAdapter(
override fun onBindViewHolder(holder: StatusViewHolder, position: Int) {
getItem(position)?.let { item ->
holder.setupWithStatus(item.second, statusListener, statusDisplayOptions)
holder.setupWithStatus(item, statusListener, statusDisplayOptions)
}
}
fun item(position: Int): Pair<Status, StatusViewData.Concrete>? {
return getItem(position)
}
companion object {
val STATUS_COMPARATOR = object : DiffUtil.ItemCallback<Pair<Status, StatusViewData.Concrete>>() {
override fun areContentsTheSame(oldItem: Pair<Status, StatusViewData.Concrete>, newItem: Pair<Status, StatusViewData.Concrete>): Boolean =
val STATUS_COMPARATOR = object : DiffUtil.ItemCallback<StatusViewData.Concrete>() {
override fun areContentsTheSame(oldItem: StatusViewData.Concrete, newItem: StatusViewData.Concrete): Boolean =
oldItem == newItem
override fun areItemsTheSame(oldItem: Pair<Status, StatusViewData.Concrete>, newItem: Pair<Status, StatusViewData.Concrete>): Boolean =
oldItem.second.id == newItem.second.id
override fun areItemsTheSame(oldItem: StatusViewData.Concrete, newItem: StatusViewData.Concrete): Boolean =
oldItem.id == newItem.id
}
}
}

View file

@ -62,15 +62,15 @@ import com.keylesspalace.tusky.viewdata.StatusViewData
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.Flow
class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concrete>>(), StatusActionListener {
class SearchStatusesFragment : SearchFragment<StatusViewData.Concrete>(), StatusActionListener {
override val data: Flow<PagingData<Pair<Status, StatusViewData.Concrete>>>
override val data: Flow<PagingData<StatusViewData.Concrete>>
get() = viewModel.statusesFlow
private val searchAdapter
get() = super.adapter as SearchStatusesAdapter
override fun createAdapter(): PagingDataAdapter<Pair<Status, StatusViewData.Concrete>, *> {
override fun createAdapter(): PagingDataAdapter<StatusViewData.Concrete, *> {
val preferences = PreferenceManager.getDefaultSharedPreferences(binding.searchRecyclerView.context)
val statusDisplayOptions = StatusDisplayOptions(
animateAvatars = preferences.getBoolean("animateGifAvatars", false),
@ -91,37 +91,37 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
}
override fun onContentHiddenChange(isShowing: Boolean, position: Int) {
searchAdapter.item(position)?.let {
searchAdapter.peek(position)?.let {
viewModel.contentHiddenChange(it, isShowing)
}
}
override fun onReply(position: Int) {
searchAdapter.item(position)?.first?.let { status ->
searchAdapter.peek(position)?.status?.let { status ->
reply(status)
}
}
override fun onFavourite(favourite: Boolean, position: Int) {
searchAdapter.item(position)?.let { status ->
searchAdapter.peek(position)?.let { status ->
viewModel.favorite(status, favourite)
}
}
override fun onBookmark(bookmark: Boolean, position: Int) {
searchAdapter.item(position)?.let { status ->
searchAdapter.peek(position)?.let { status ->
viewModel.bookmark(status, bookmark)
}
}
override fun onMore(view: View, position: Int) {
searchAdapter.item(position)?.first?.let {
searchAdapter.peek(position)?.status?.let {
more(it, view, position)
}
}
override fun onViewMedia(position: Int, attachmentIndex: Int, view: View?) {
searchAdapter.item(position)?.first?.actionableStatus?.let { actionable ->
searchAdapter.peek(position)?.status?.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)
@ -149,20 +149,20 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
}
override fun onViewThread(position: Int) {
searchAdapter.item(position)?.first?.let { status ->
searchAdapter.peek(position)?.status?.let { status ->
val actionableStatus = status.actionableStatus
bottomSheetActivity?.viewThread(actionableStatus.id, actionableStatus.url)
}
}
override fun onOpenReblog(position: Int) {
searchAdapter.item(position)?.first?.let { status ->
searchAdapter.peek(position)?.status?.let { status ->
bottomSheetActivity?.viewAccount(status.account.id)
}
}
override fun onExpandedChange(expanded: Boolean, position: Int) {
searchAdapter.item(position)?.let {
searchAdapter.peek(position)?.let {
viewModel.expandedChange(it, expanded)
}
}
@ -172,25 +172,25 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
}
override fun onContentCollapsedChange(isCollapsed: Boolean, position: Int) {
searchAdapter.item(position)?.let {
searchAdapter.peek(position)?.let {
viewModel.collapsedChange(it, isCollapsed)
}
}
override fun onVoteInPoll(position: Int, choices: MutableList<Int>) {
searchAdapter.item(position)?.let {
searchAdapter.peek(position)?.let {
viewModel.voteInPoll(it, choices)
}
}
private fun removeItem(position: Int) {
searchAdapter.item(position)?.let {
searchAdapter.peek(position)?.let {
viewModel.removeItem(it)
}
}
override fun onReblog(reblog: Boolean, position: Int) {
searchAdapter.item(position)?.let { status ->
searchAdapter.peek(position)?.let { status ->
viewModel.reblog(status, reblog)
}
}
@ -316,7 +316,7 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
return@setOnMenuItemClickListener true
}
R.id.status_mute_conversation -> {
searchAdapter.item(position)?.let { foundStatus ->
searchAdapter.peek(position)?.let { foundStatus ->
viewModel.muteConversation(foundStatus, status.muted != true)
}
return@setOnMenuItemClickListener true