2019-07-19 20:10:20 +02:00
|
|
|
package com.keylesspalace.tusky.components.search
|
|
|
|
|
|
|
|
import android.util.Log
|
|
|
|
import androidx.lifecycle.LiveData
|
|
|
|
import androidx.lifecycle.MutableLiveData
|
|
|
|
import androidx.paging.PagedList
|
|
|
|
import com.keylesspalace.tusky.components.search.adapter.SearchRepository
|
|
|
|
import com.keylesspalace.tusky.db.AccountEntity
|
|
|
|
import com.keylesspalace.tusky.db.AccountManager
|
2019-08-28 19:54:46 +02:00
|
|
|
import com.keylesspalace.tusky.entity.*
|
2020-01-13 13:57:44 +01:00
|
|
|
import com.keylesspalace.tusky.entity.Status
|
2019-07-19 20:10:20 +02:00
|
|
|
import com.keylesspalace.tusky.network.MastodonApi
|
|
|
|
import com.keylesspalace.tusky.network.TimelineCases
|
2020-01-13 13:57:44 +01:00
|
|
|
import com.keylesspalace.tusky.util.*
|
2019-07-19 20:10:20 +02:00
|
|
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
2019-08-28 19:54:46 +02:00
|
|
|
import io.reactivex.Single
|
2019-07-19 20:10:20 +02:00
|
|
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
|
|
|
import javax.inject.Inject
|
|
|
|
|
|
|
|
class SearchViewModel @Inject constructor(
|
|
|
|
mastodonApi: MastodonApi,
|
|
|
|
private val timelineCases: TimelineCases,
|
2020-01-13 13:57:44 +01:00
|
|
|
private val accountManager: AccountManager
|
|
|
|
) : RxAwareViewModel() {
|
2019-07-19 20:10:20 +02:00
|
|
|
|
2019-11-17 20:58:54 +01:00
|
|
|
var currentQuery: String = ""
|
2019-07-19 20:10:20 +02:00
|
|
|
|
|
|
|
var activeAccount: AccountEntity?
|
|
|
|
get() = accountManager.activeAccount
|
|
|
|
set(value) {
|
|
|
|
accountManager.activeAccount = value
|
|
|
|
}
|
|
|
|
|
2020-01-13 13:57:44 +01:00
|
|
|
val mediaPreviewEnabled = activeAccount?.mediaPreviewEnabled ?: false
|
|
|
|
val alwaysShowSensitiveMedia = activeAccount?.alwaysShowSensitiveMedia ?: false
|
|
|
|
val alwaysOpenSpoiler = activeAccount?.alwaysOpenSpoiler ?: false
|
2019-07-19 20:10:20 +02:00
|
|
|
|
|
|
|
private val statusesRepository = SearchRepository<Pair<Status, StatusViewData.Concrete>>(mastodonApi)
|
|
|
|
private val accountsRepository = SearchRepository<Account>(mastodonApi)
|
|
|
|
private val hashtagsRepository = SearchRepository<HashTag>(mastodonApi)
|
|
|
|
|
|
|
|
private val repoResultStatus = MutableLiveData<Listing<Pair<Status, StatusViewData.Concrete>>>()
|
2020-01-13 13:57:44 +01:00
|
|
|
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 }
|
2019-07-19 20:10:20 +02:00
|
|
|
|
|
|
|
private val repoResultAccount = MutableLiveData<Listing<Account>>()
|
2020-01-13 13:57:44 +01:00
|
|
|
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 }
|
2019-07-19 20:10:20 +02:00
|
|
|
|
|
|
|
private val repoResultHashTag = MutableLiveData<Listing<HashTag>>()
|
2020-01-13 13:57:44 +01:00
|
|
|
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 }
|
2019-07-19 20:10:20 +02:00
|
|
|
|
|
|
|
private val loadedStatuses = ArrayList<Pair<Status, StatusViewData.Concrete>>()
|
2019-11-17 20:58:54 +01:00
|
|
|
fun search(query: String) {
|
2019-07-19 20:10:20 +02:00
|
|
|
loadedStatuses.clear()
|
|
|
|
repoResultStatus.value = statusesRepository.getSearchData(SearchType.Status, query, disposables, initialItems = loadedStatuses) {
|
2020-01-13 13:57:44 +01:00
|
|
|
it?.statuses?.map { status -> Pair(status, ViewDataUtils.statusToViewData(status, alwaysShowSensitiveMedia, alwaysOpenSpoiler)!!) }
|
|
|
|
.orEmpty()
|
2019-07-19 20:10:20 +02:00
|
|
|
.apply {
|
|
|
|
loadedStatuses.addAll(this)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
repoResultAccount.value = accountsRepository.getSearchData(SearchType.Account, query, disposables) {
|
2020-01-13 13:57:44 +01:00
|
|
|
it?.accounts.orEmpty()
|
2019-07-19 20:10:20 +02:00
|
|
|
}
|
2019-11-17 20:58:54 +01:00
|
|
|
val hashtagQuery = if (query.startsWith("#")) query else "#$query"
|
2019-08-04 20:32:44 +02:00
|
|
|
repoResultHashTag.value =
|
|
|
|
hashtagsRepository.getSearchData(SearchType.Hashtag, hashtagQuery, disposables) {
|
2020-01-13 13:57:44 +01:00
|
|
|
it?.hashtags.orEmpty()
|
2019-08-04 20:32:44 +02:00
|
|
|
}
|
2019-07-19 20:10:20 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fun removeItem(status: Pair<Status, StatusViewData.Concrete>) {
|
|
|
|
timelineCases.delete(status.first.id)
|
2019-11-17 20:59:13 +01:00
|
|
|
.subscribe({
|
|
|
|
if (loadedStatuses.remove(status))
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}, {
|
|
|
|
err -> Log.d(TAG, "Failed to delete status", err)
|
|
|
|
})
|
2020-01-02 18:50:58 +01:00
|
|
|
.autoDispose()
|
2019-11-17 20:59:13 +01:00
|
|
|
|
2019-07-19 20:10:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fun expandedChange(status: Pair<Status, StatusViewData.Concrete>, expanded: Boolean) {
|
|
|
|
val idx = loadedStatuses.indexOf(status)
|
|
|
|
if (idx >= 0) {
|
|
|
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setIsExpanded(expanded).createStatusViewData())
|
|
|
|
loadedStatuses[idx] = newPair
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun reblog(status: Pair<Status, StatusViewData.Concrete>, reblog: Boolean) {
|
2020-01-02 18:50:58 +01:00
|
|
|
timelineCases.reblog(status.first, reblog)
|
2019-07-19 20:10:20 +02:00
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(
|
|
|
|
{ setRebloggedForStatus(status, reblog) },
|
|
|
|
{ err -> Log.d(TAG, "Failed to reblog status ${status.first.id}", err) }
|
|
|
|
)
|
2020-01-02 18:50:58 +01:00
|
|
|
.autoDispose()
|
2019-07-19 20:10:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun setRebloggedForStatus(status: Pair<Status, StatusViewData.Concrete>, reblog: Boolean) {
|
|
|
|
status.first.reblogged = reblog
|
|
|
|
status.first.reblog?.reblogged = reblog
|
|
|
|
|
|
|
|
val idx = loadedStatuses.indexOf(status)
|
|
|
|
if (idx >= 0) {
|
|
|
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setReblogged(reblog).createStatusViewData())
|
|
|
|
loadedStatuses[idx] = newPair
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun contentHiddenChange(status: Pair<Status, StatusViewData.Concrete>, isShowing: Boolean) {
|
|
|
|
val idx = loadedStatuses.indexOf(status)
|
|
|
|
if (idx >= 0) {
|
|
|
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setIsShowingSensitiveContent(isShowing).createStatusViewData())
|
|
|
|
loadedStatuses[idx] = newPair
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun collapsedChange(status: Pair<Status, StatusViewData.Concrete>, collapsed: Boolean) {
|
|
|
|
val idx = loadedStatuses.indexOf(status)
|
|
|
|
if (idx >= 0) {
|
|
|
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setCollapsed(collapsed).createStatusViewData())
|
|
|
|
loadedStatuses[idx] = newPair
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun voteInPoll(status: Pair<Status, StatusViewData.Concrete>, choices: MutableList<Int>) {
|
|
|
|
val votedPoll = status.first.actionableStatus.poll!!.votedCopy(choices)
|
|
|
|
updateStatus(status, votedPoll)
|
2020-01-02 18:50:58 +01:00
|
|
|
timelineCases.voteInPoll(status.first, choices)
|
2019-07-19 20:10:20 +02:00
|
|
|
.observeOn(AndroidSchedulers.mainThread())
|
|
|
|
.subscribe(
|
|
|
|
{ newPoll -> updateStatus(status, newPoll) },
|
|
|
|
{ t ->
|
|
|
|
Log.d(TAG,
|
|
|
|
"Failed to vote in poll: ${status.first.id}", t)
|
|
|
|
}
|
2020-01-02 18:50:58 +01:00
|
|
|
)
|
|
|
|
.autoDispose()
|
2019-07-19 20:10:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun updateStatus(status: Pair<Status, StatusViewData.Concrete>, newPoll: Poll) {
|
|
|
|
val idx = loadedStatuses.indexOf(status)
|
|
|
|
if (idx >= 0) {
|
|
|
|
|
|
|
|
val newViewData = StatusViewData.Builder(status.second)
|
|
|
|
.setPoll(newPoll)
|
|
|
|
.createStatusViewData()
|
|
|
|
loadedStatuses[idx] = Pair(status.first, newViewData)
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun favorite(status: Pair<Status, StatusViewData.Concrete>, isFavorited: Boolean) {
|
|
|
|
val idx = loadedStatuses.indexOf(status)
|
|
|
|
if (idx >= 0) {
|
|
|
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setFavourited(isFavorited).createStatusViewData())
|
|
|
|
loadedStatuses[idx] = newPair
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}
|
2020-01-02 18:50:58 +01:00
|
|
|
timelineCases.favourite(status.first, isFavorited)
|
2019-07-19 20:10:20 +02:00
|
|
|
.onErrorReturnItem(status.first)
|
2020-01-02 18:50:58 +01:00
|
|
|
.subscribe()
|
|
|
|
.autoDispose()
|
2019-07-19 20:10:20 +02:00
|
|
|
}
|
|
|
|
|
2019-11-19 10:15:32 +01:00
|
|
|
fun bookmark(status: Pair<Status, StatusViewData.Concrete>, isBookmarked: Boolean) {
|
|
|
|
val idx = loadedStatuses.indexOf(status)
|
|
|
|
if (idx >= 0) {
|
2020-01-13 13:57:44 +01:00
|
|
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setBookmarked(isBookmarked).createStatusViewData())
|
2019-11-19 10:15:32 +01:00
|
|
|
loadedStatuses[idx] = newPair
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}
|
2020-01-13 13:57:44 +01:00
|
|
|
timelineCases.bookmark(status.first, isBookmarked)
|
2019-11-19 10:15:32 +01:00
|
|
|
.onErrorReturnItem(status.first)
|
2020-01-02 18:50:58 +01:00
|
|
|
.subscribe()
|
|
|
|
.autoDispose()
|
2019-11-19 10:15:32 +01:00
|
|
|
}
|
|
|
|
|
2019-07-19 20:10:20 +02:00
|
|
|
fun getAllAccountsOrderedByActive(): List<AccountEntity> {
|
|
|
|
return accountManager.getAllAccountsOrderedByActive()
|
|
|
|
}
|
|
|
|
|
2021-05-09 18:37:41 +02:00
|
|
|
fun muteAccount(accountId: String, notifications: Boolean, duration: Int?) {
|
2021-01-15 21:05:36 +01:00
|
|
|
timelineCases.mute(accountId, notifications, duration)
|
2019-07-19 20:10:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fun pinAccount(status: Status, isPin: Boolean) {
|
|
|
|
timelineCases.pin(status, isPin)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun blockAccount(accountId: String) {
|
|
|
|
timelineCases.block(accountId)
|
|
|
|
}
|
|
|
|
|
2019-08-28 19:54:46 +02:00
|
|
|
fun deleteStatus(id: String): Single<DeletedStatus> {
|
|
|
|
return timelineCases.delete(id)
|
2019-07-19 20:10:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fun retryAllSearches() {
|
|
|
|
search(currentQuery)
|
|
|
|
}
|
|
|
|
|
2020-03-24 21:06:04 +01:00
|
|
|
fun muteConversation(status: Pair<Status, StatusViewData.Concrete>, mute: Boolean) {
|
|
|
|
val idx = loadedStatuses.indexOf(status)
|
|
|
|
if (idx >= 0) {
|
|
|
|
val newPair = Pair(status.first, StatusViewData.Builder(status.second).setMuted(mute).createStatusViewData())
|
|
|
|
loadedStatuses[idx] = newPair
|
|
|
|
repoResultStatus.value?.refresh?.invoke()
|
|
|
|
}
|
|
|
|
timelineCases.muteConversation(status.first, mute)
|
|
|
|
.onErrorReturnItem(status.first)
|
|
|
|
.subscribe()
|
|
|
|
.autoDispose()
|
|
|
|
}
|
2019-07-19 20:10:20 +02:00
|
|
|
|
|
|
|
companion object {
|
|
|
|
private const val TAG = "SearchViewModel"
|
|
|
|
}
|
|
|
|
}
|