Yuito-app-android/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt

203 lines
7.6 KiB
Kotlin
Raw Normal View History

/* Copyright 2019 Tusky Contributors
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <https://www.gnu.org/licenses>. */
package com.keylesspalace.tusky
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.Lifecycle
import autodispose2.androidx.lifecycle.autoDispose
import com.keylesspalace.tusky.appstore.EventHub
import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.snackbar.Snackbar
import com.keylesspalace.tusky.components.timeline.TimelineFragment
Timeline paging (#2238) * first setup * network timeline paging / improvements * rename classes / move to correct package * remove unused class TimelineAdapter * some code cleanup * remove TimelineRepository, put mapper functions in TimelineTypeMappers.kt * add db migration * cleanup unused code * bugfix * make default timeline settings work again * fix pinning statuses from timeline * fix network timeline * respect account settings in NetworkTimelineRemoteMediator * respect account settings in NetworkTimelineRemoteMediator * update license headers * show error view when an error occurs * cleanup some todos * fix db migration * fix changing mediaPreviewEnabled setting * fix "load more" button appearing on top of timeline * fix filtering and other bugs * cleanup cache after 14 days * fix TimelineDAOTest * fix code formatting * add NetworkTimeline unit tests * add CachedTimeline unit tests * fix code formatting * move TimelineDaoTest to unit tests * implement removeAllByInstance for CachedTimelineViewModel * fix code formatting * fix bug in TimelineDao.deleteAllFromInstance * improve loading more statuses in NetworkTimelineViewModel * improve loading more statuses in NetworkTimelineViewModel * fix bug where empty state was shown too soon * reload top of cached timeline on app start * improve CachedTimelineRemoteMediator and Tests * improve cached timeline tests * fix some more todos * implement TimelineFragment.removeItem * fix ListStatusAccessibilityDelegate * fix crash in NetworkTimelineViewModel.loadMore * fix default state of collapsible statuses * fix default state of collapsible statuses -tests * fix showing/hiding media in the timeline * get rid of some not-null assertion operators in TimelineTypeMappers * fix tests * error handling in CachedTimelineViewModel.loadMore * keep local status state when refreshing cached statuses * keep local status state when refreshing network timeline statuses * show placeholder loading state in cached timeline * better comments, some code cleanup * add TimelineViewModelTest, improve code, fix bug * fix ktlint * fix voting in boosted polls * code improvement
2022-01-11 19:00:29 +01:00
import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel.Kind
import com.keylesspalace.tusky.databinding.ActivityStatuslistBinding
import com.keylesspalace.tusky.di.ViewModelFactory
import com.keylesspalace.tusky.util.viewBinding
import dagger.android.DispatchingAndroidInjector
import dagger.android.HasAndroidInjector
import kotlinx.coroutines.launch
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import net.accelf.yuito.QuickTootViewModel
2020-01-27 17:11:10 +01:00
import javax.inject.Inject
class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
@Inject
lateinit var eventHub: EventHub
@Inject
lateinit var viewModelFactory: ViewModelFactory
private val quickTootViewModel: QuickTootViewModel by viewModels{ viewModelFactory }
private val binding: ActivityStatuslistBinding by viewBinding(ActivityStatuslistBinding::inflate)
private lateinit var kind: Kind
private var hashtag: String? = null
private var followTagItem: MenuItem? = null
private var unfollowTagItem: MenuItem? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
setSupportActionBar(binding.includedToolbar.toolbar)
kind = Kind.valueOf(intent.getStringExtra(EXTRA_KIND)!!)
val listId = intent.getStringExtra(EXTRA_LIST_ID)
hashtag = intent.getStringExtra(EXTRA_HASHTAG)
val title = when (kind) {
Kind.FAVOURITES -> getString(R.string.title_favourites)
Kind.BOOKMARKS -> getString(R.string.title_bookmarks)
Kind.TAG -> getString(R.string.title_tag).format(hashtag)
else -> intent.getStringExtra(EXTRA_LIST_TITLE)
}
supportActionBar?.run {
setTitle(title)
setDisplayHomeAsUpEnabled(true)
setDisplayShowHomeEnabled(true)
}
if (supportFragmentManager.findFragmentById(R.id.fragmentContainer) == null) {
supportFragmentManager.commit {
val fragment = if (kind == Kind.TAG) {
TimelineFragment.newHashtagInstance(listOf(hashtag!!))
} else {
TimelineFragment.newInstance(kind, listId)
}
replace(R.id.fragmentContainer, fragment)
}
}
binding.viewQuickToot.attachViewModel(quickTootViewModel, this)
eventHub.events
.observeOn(AndroidSchedulers.mainThread())
.autoDispose(this, Lifecycle.Event.ON_DESTROY)
.subscribe(binding.viewQuickToot::handleEvent)
binding.floatingBtn.setOnClickListener(binding.viewQuickToot::onFABClicked)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val tag = hashtag
if (kind == Kind.TAG && tag != null) {
lifecycleScope.launch {
mastodonApi.tag(tag).fold(
{ tagEntity ->
menuInflater.inflate(R.menu.view_hashtag_toolbar, menu)
followTagItem = menu.findItem(R.id.action_follow_hashtag)
unfollowTagItem = menu.findItem(R.id.action_unfollow_hashtag)
followTagItem?.isVisible = tagEntity.following == false
unfollowTagItem?.isVisible = tagEntity.following == true
followTagItem?.setOnMenuItemClickListener { followTag() }
unfollowTagItem?.setOnMenuItemClickListener { unfollowTag() }
},
{
Log.w(TAG, "Failed to query tag #$tag", it)
}
)
}
}
return super.onCreateOptionsMenu(menu)
}
private fun followTag(): Boolean {
val tag = hashtag
if (tag != null) {
lifecycleScope.launch {
mastodonApi.followTag(tag).fold(
{
followTagItem?.isVisible = false
unfollowTagItem?.isVisible = true
},
{
Snackbar.make(binding.root, getString(R.string.error_following_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
Log.e(TAG, "Failed to follow #$tag", it)
}
)
}
}
return true
}
private fun unfollowTag(): Boolean {
val tag = hashtag
if (tag != null) {
lifecycleScope.launch {
mastodonApi.unfollowTag(tag).fold(
{
followTagItem?.isVisible = true
unfollowTagItem?.isVisible = false
},
{
Snackbar.make(binding.root, getString(R.string.error_unfollowing_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
Log.e(TAG, "Failed to unfollow #$tag", it)
}
)
}
}
return true
}
override fun androidInjector() = dispatchingAndroidInjector
companion object {
private const val EXTRA_KIND = "kind"
private const val EXTRA_LIST_ID = "id"
private const val EXTRA_LIST_TITLE = "title"
private const val EXTRA_HASHTAG = "tag"
const val TAG = "StatusListActivity"
fun newFavouritesIntent(context: Context) =
Intent(context, StatusListActivity::class.java).apply {
putExtra(EXTRA_KIND, Kind.FAVOURITES.name)
}
fun newBookmarksIntent(context: Context) =
Intent(context, StatusListActivity::class.java).apply {
putExtra(EXTRA_KIND, Kind.BOOKMARKS.name)
}
fun newListIntent(context: Context, listId: String, listTitle: String) =
Intent(context, StatusListActivity::class.java).apply {
putExtra(EXTRA_KIND, Kind.LIST.name)
putExtra(EXTRA_LIST_ID, listId)
putExtra(EXTRA_LIST_TITLE, listTitle)
}
@JvmStatic
fun newHashtagIntent(context: Context, hashtag: String) =
Intent(context, StatusListActivity::class.java).apply {
putExtra(EXTRA_KIND, Kind.TAG.name)
putExtra(EXTRA_HASHTAG, hashtag)
}
}
}