mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-30 22:34:02 +01:00
Fix space problem of categoriesPresenter.kt
This commit is contained in:
parent
1e31497f10
commit
75ec41bdb5
1 changed files with 282 additions and 282 deletions
|
|
@ -28,302 +28,302 @@ import javax.inject.Singleton
|
|||
*/
|
||||
@Singleton
|
||||
class CategoriesPresenter
|
||||
@Inject
|
||||
constructor(
|
||||
private val repository: UploadRepository,
|
||||
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler,
|
||||
) : CategoriesContract.UserActionListener {
|
||||
companion object {
|
||||
private val DUMMY: CategoriesContract.View = proxy()
|
||||
}
|
||||
|
||||
var view = DUMMY
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val searchTerms = PublishSubject.create<String>()
|
||||
private var categoryList: MutableLiveData<List<CategoryItem>> = MutableLiveData()
|
||||
|
||||
/**
|
||||
* Current media
|
||||
*/
|
||||
private var media: Media? = null
|
||||
|
||||
/**
|
||||
* helper class for editing categories
|
||||
*/
|
||||
@Inject
|
||||
lateinit var categoryEditHelper: CategoryEditHelper
|
||||
constructor(
|
||||
private val repository: UploadRepository,
|
||||
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler,
|
||||
) : CategoriesContract.UserActionListener {
|
||||
companion object {
|
||||
private val DUMMY: CategoriesContract.View = proxy()
|
||||
}
|
||||
|
||||
@SuppressLint("TimberArgCount")
|
||||
override fun onAttachView(view: CategoriesContract.View) {
|
||||
this.view = view
|
||||
compositeDisposable.add(
|
||||
searchTerms
|
||||
.observeOn(mainThreadScheduler)
|
||||
.doOnNext {
|
||||
view.showProgress(true)
|
||||
}.switchMap(::searchResults)
|
||||
.map { repository.selectedCategories + it }
|
||||
.map { it.distinctBy { categoryItem -> categoryItem.name } }
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(
|
||||
{
|
||||
setCategoryListValue(it)
|
||||
view.showProgress(false)
|
||||
if (it.isEmpty() && !isInitialLoad) {
|
||||
view.showError(R.string.no_categories_found)
|
||||
}
|
||||
},
|
||||
{ t: Throwable? ->
|
||||
view.showProgress(false)
|
||||
view.showError(R.string.no_categories_found)
|
||||
Timber.e(t)
|
||||
},
|
||||
),
|
||||
)
|
||||
var view = DUMMY
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val searchTerms = PublishSubject.create<String>()
|
||||
private var categoryList: MutableLiveData<List<CategoryItem>> = MutableLiveData()
|
||||
|
||||
//isInitialLoad = false
|
||||
}
|
||||
/**
|
||||
* Current media
|
||||
*/
|
||||
private var media: Media? = null
|
||||
|
||||
private var isInitialLoad = true //avoid initial empty content of edittext lead to showError
|
||||
/**
|
||||
* helper class for editing categories
|
||||
*/
|
||||
@Inject
|
||||
lateinit var categoryEditHelper: CategoryEditHelper
|
||||
|
||||
|
||||
/**
|
||||
* If media is null : Fetches categories from server according to the term
|
||||
* Else : Fetches existing categories by their name, fetches categories from server according
|
||||
* to the term and combines both in a list
|
||||
*/
|
||||
private fun searchResults(term: String): Observable<List<CategoryItem>>? {
|
||||
if (media == null) {
|
||||
return repository
|
||||
.searchAll(term, getImageTitleList(), repository.selectedDepictions)
|
||||
.subscribeOn(ioScheduler)
|
||||
.map {
|
||||
it.filter { categoryItem ->
|
||||
!repository.isSpammyCategory(categoryItem.name) ||
|
||||
categoryItem.name == term
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Observable
|
||||
.zip(
|
||||
repository
|
||||
.getCategories(repository.selectedExistingCategories)
|
||||
.map { list ->
|
||||
list.map {
|
||||
CategoryItem(it.name, it.description, it.thumbnail, true)
|
||||
@SuppressLint("TimberArgCount")
|
||||
override fun onAttachView(view: CategoriesContract.View) {
|
||||
this.view = view
|
||||
compositeDisposable.add(
|
||||
searchTerms
|
||||
.observeOn(mainThreadScheduler)
|
||||
.doOnNext {
|
||||
view.showProgress(true)
|
||||
}.switchMap(::searchResults)
|
||||
.map { repository.selectedCategories + it }
|
||||
.map { it.distinctBy { categoryItem -> categoryItem.name } }
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(
|
||||
{
|
||||
setCategoryListValue(it)
|
||||
view.showProgress(false)
|
||||
if (it.isEmpty() && !isInitialLoad) {
|
||||
view.showError(R.string.no_categories_found)
|
||||
}
|
||||
},
|
||||
repository.searchAll(term, getImageTitleList(), repository.selectedDepictions),
|
||||
) { it1, it2 ->
|
||||
it1 + it2
|
||||
}.subscribeOn(ioScheduler)
|
||||
.map {
|
||||
it.filter { categoryItem ->
|
||||
!repository.isSpammyCategory(categoryItem.name) ||
|
||||
categoryItem.name == term
|
||||
}
|
||||
}.map { it.filterNot { categoryItem -> categoryItem.thumbnail == "hidden" } }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachView() {
|
||||
view = DUMMY
|
||||
compositeDisposable.clear()
|
||||
isInitialLoad = true
|
||||
}
|
||||
|
||||
/**
|
||||
* asks the repository to fetch categories for the query
|
||||
* @param query
|
||||
*/
|
||||
override fun searchForCategories(query: String) {
|
||||
if (query.isBlank()) {
|
||||
if (!isInitialLoad) {
|
||||
view.showError(R.string.no_categories_found)
|
||||
}
|
||||
return
|
||||
}
|
||||
isInitialLoad = false
|
||||
searchTerms.onNext(query)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns image title list from UploadItem
|
||||
* @return
|
||||
*/
|
||||
private fun getImageTitleList(): List<String> =
|
||||
repository.uploads
|
||||
.map { it.uploadMediaDetails[0].captionText }
|
||||
.filterNot { TextUtils.isEmpty(it) }
|
||||
|
||||
/**
|
||||
* Verifies the number of categories selected, prompts the user if none selected
|
||||
*/
|
||||
override fun verifyCategories() {
|
||||
val selectedCategories = repository.selectedCategories
|
||||
if (selectedCategories.isNotEmpty()) {
|
||||
repository.setSelectedCategories(selectedCategories.map { it.name })
|
||||
view.goToNextScreen()
|
||||
} else {
|
||||
view.showNoCategorySelected()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ask repository to handle category clicked
|
||||
*
|
||||
* @param categoryItem
|
||||
*/
|
||||
override fun onCategoryItemClicked(categoryItem: CategoryItem) {
|
||||
repository.onCategoryClicked(categoryItem, media)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches view and media
|
||||
*/
|
||||
override fun onAttachViewWithMedia(
|
||||
view: CategoriesContract.View,
|
||||
media: Media,
|
||||
) {
|
||||
this.view = view
|
||||
this.media = media
|
||||
repository.selectedExistingCategories = view.existingCategories
|
||||
compositeDisposable.add(
|
||||
searchTerms
|
||||
.observeOn(mainThreadScheduler)
|
||||
.doOnNext {
|
||||
view.showProgress(true)
|
||||
}.switchMap(::searchResults)
|
||||
.map { repository.selectedCategories + it }
|
||||
.map { it.distinctBy { categoryItem -> categoryItem.name } }
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(
|
||||
{
|
||||
setCategoryListValue(it)
|
||||
view.showProgress(false)
|
||||
if (it.isEmpty() && !isInitialLoad) {
|
||||
{ t: Throwable? ->
|
||||
view.showProgress(false)
|
||||
view.showError(R.string.no_categories_found)
|
||||
Timber.e(t)
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
//isInitialLoad = false
|
||||
}
|
||||
|
||||
private var isInitialLoad = true //avoid initial empty content of edittext lead to showError
|
||||
|
||||
|
||||
/**
|
||||
* If media is null : Fetches categories from server according to the term
|
||||
* Else : Fetches existing categories by their name, fetches categories from server according
|
||||
* to the term and combines both in a list
|
||||
*/
|
||||
private fun searchResults(term: String): Observable<List<CategoryItem>>? {
|
||||
if (media == null) {
|
||||
return repository
|
||||
.searchAll(term, getImageTitleList(), repository.selectedDepictions)
|
||||
.subscribeOn(ioScheduler)
|
||||
.map {
|
||||
it.filter { categoryItem ->
|
||||
!repository.isSpammyCategory(categoryItem.name) ||
|
||||
categoryItem.name == term
|
||||
}
|
||||
},
|
||||
{ t: Throwable? ->
|
||||
view.showProgress(false)
|
||||
view.showError(R.string.no_categories_found)
|
||||
Timber.e(t)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears previous selections
|
||||
*/
|
||||
override fun clearPreviousSelection() {
|
||||
repository.cleanup()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selected categories and send them for posting to the server
|
||||
*
|
||||
* @param media media
|
||||
* @param wikiText current WikiText from server
|
||||
*/
|
||||
override fun updateCategories(
|
||||
media: Media,
|
||||
wikiText: String,
|
||||
) {
|
||||
// check if view.existingCategories is null
|
||||
if (repository.selectedCategories.isNotEmpty() ||
|
||||
(view.existingCategories != null && repository.selectedExistingCategories.size != view.existingCategories.size)
|
||||
) {
|
||||
val selectedCategories: MutableList<String> =
|
||||
(
|
||||
repository.selectedCategories.map { it.name }.toMutableList() +
|
||||
repository.selectedExistingCategories
|
||||
).toMutableList()
|
||||
|
||||
if (selectedCategories.isNotEmpty()) {
|
||||
view.showProgressDialog()
|
||||
|
||||
try {
|
||||
compositeDisposable.add(
|
||||
categoryEditHelper
|
||||
.makeCategoryEdit(
|
||||
view.fragmentContext,
|
||||
media,
|
||||
selectedCategories,
|
||||
wikiText,
|
||||
).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
Timber.d("Categories are added.")
|
||||
media.addedCategories = selectedCategories
|
||||
repository.cleanup()
|
||||
view.dismissProgressDialog()
|
||||
view.refreshCategories()
|
||||
view.goBackToPreviousScreen()
|
||||
}, {
|
||||
Timber.e(
|
||||
"Failed to update categories",
|
||||
)
|
||||
}),
|
||||
)
|
||||
} catch (e: InvalidLoginTokenException) {
|
||||
view.navigateToLoginScreen()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Observable
|
||||
.zip(
|
||||
repository
|
||||
.getCategories(repository.selectedExistingCategories)
|
||||
.map { list ->
|
||||
list.map {
|
||||
CategoryItem(it.name, it.description, it.thumbnail, true)
|
||||
}
|
||||
},
|
||||
repository.searchAll(term, getImageTitleList(), repository.selectedDepictions),
|
||||
) { it1, it2 ->
|
||||
it1 + it2
|
||||
}.subscribeOn(ioScheduler)
|
||||
.map {
|
||||
it.filter { categoryItem ->
|
||||
!repository.isSpammyCategory(categoryItem.name) ||
|
||||
categoryItem.name == term
|
||||
}
|
||||
}.map { it.filterNot { categoryItem -> categoryItem.thumbnail == "hidden" } }
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
override fun onDetachView() {
|
||||
view = DUMMY
|
||||
compositeDisposable.clear()
|
||||
isInitialLoad = true
|
||||
}
|
||||
|
||||
/**
|
||||
* asks the repository to fetch categories for the query
|
||||
* @param query
|
||||
*/
|
||||
override fun searchForCategories(query: String) {
|
||||
if (query.isBlank()) {
|
||||
if (!isInitialLoad) {
|
||||
view.showError(R.string.no_categories_found)
|
||||
}
|
||||
return
|
||||
}
|
||||
isInitialLoad = false
|
||||
searchTerms.onNext(query)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns image title list from UploadItem
|
||||
* @return
|
||||
*/
|
||||
private fun getImageTitleList(): List<String> =
|
||||
repository.uploads
|
||||
.map { it.uploadMediaDetails[0].captionText }
|
||||
.filterNot { TextUtils.isEmpty(it) }
|
||||
|
||||
/**
|
||||
* Verifies the number of categories selected, prompts the user if none selected
|
||||
*/
|
||||
override fun verifyCategories() {
|
||||
val selectedCategories = repository.selectedCategories
|
||||
if (selectedCategories.isNotEmpty()) {
|
||||
repository.setSelectedCategories(selectedCategories.map { it.name })
|
||||
view.goToNextScreen()
|
||||
} else {
|
||||
view.showNoCategorySelected()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ask repository to handle category clicked
|
||||
*
|
||||
* @param categoryItem
|
||||
*/
|
||||
override fun onCategoryItemClicked(categoryItem: CategoryItem) {
|
||||
repository.onCategoryClicked(categoryItem, media)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches view and media
|
||||
*/
|
||||
override fun onAttachViewWithMedia(
|
||||
view: CategoriesContract.View,
|
||||
media: Media,
|
||||
) {
|
||||
this.view = view
|
||||
this.media = media
|
||||
repository.selectedExistingCategories = view.existingCategories
|
||||
compositeDisposable.add(
|
||||
searchTerms
|
||||
.observeOn(mainThreadScheduler)
|
||||
.doOnNext {
|
||||
view.showProgress(true)
|
||||
}.switchMap(::searchResults)
|
||||
.map { repository.selectedCategories + it }
|
||||
.map { it.distinctBy { categoryItem -> categoryItem.name } }
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(
|
||||
{
|
||||
setCategoryListValue(it)
|
||||
view.showProgress(false)
|
||||
if (it.isEmpty() && !isInitialLoad) {
|
||||
view.showError(R.string.no_categories_found)
|
||||
}
|
||||
},
|
||||
{ t: Throwable? ->
|
||||
view.showProgress(false)
|
||||
view.showError(R.string.no_categories_found)
|
||||
Timber.e(t)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears previous selections
|
||||
*/
|
||||
override fun clearPreviousSelection() {
|
||||
repository.cleanup()
|
||||
view.showNoCategorySelected()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects each [CategoryItem] in a given list as if they were clicked by the user by calling
|
||||
* [onCategoryItemClicked] for each category and adding the category to [categoryList]
|
||||
*/
|
||||
private fun selectNewCategories(toSelect: List<CategoryItem>) {
|
||||
toSelect.forEach {
|
||||
it.isSelected = true
|
||||
repository.onCategoryClicked(it, media)
|
||||
}
|
||||
|
||||
// Add the new selections to the list of category items so that the selections appear
|
||||
// immediately (i.e. without any search term queries)
|
||||
categoryList.value
|
||||
?.toMutableList()
|
||||
?.let { toSelect + it }
|
||||
?.distinctBy(CategoryItem::name)
|
||||
?.let { setCategoryListValue(it) }
|
||||
}
|
||||
/**
|
||||
* Gets the selected categories and send them for posting to the server
|
||||
*
|
||||
* @param media media
|
||||
* @param wikiText current WikiText from server
|
||||
*/
|
||||
override fun updateCategories(
|
||||
media: Media,
|
||||
wikiText: String,
|
||||
) {
|
||||
// check if view.existingCategories is null
|
||||
if (repository.selectedCategories.isNotEmpty() ||
|
||||
(view.existingCategories != null && repository.selectedExistingCategories.size != view.existingCategories.size)
|
||||
) {
|
||||
val selectedCategories: MutableList<String> =
|
||||
(
|
||||
repository.selectedCategories.map { it.name }.toMutableList() +
|
||||
repository.selectedExistingCategories
|
||||
).toMutableList()
|
||||
|
||||
/**
|
||||
* Livedata being used to observe category list inside
|
||||
* @see UploadCategoriesFragment
|
||||
* Any changes to category list reflect immediately to the adapter list
|
||||
*/
|
||||
override fun getCategories(): LiveData<List<CategoryItem>> = categoryList
|
||||
if (selectedCategories.isNotEmpty()) {
|
||||
view.showProgressDialog()
|
||||
|
||||
/**
|
||||
* needed for tests
|
||||
*/
|
||||
fun setCategoryList(categoryList: MutableLiveData<List<CategoryItem>>) {
|
||||
this.categoryList = categoryList
|
||||
}
|
||||
try {
|
||||
compositeDisposable.add(
|
||||
categoryEditHelper
|
||||
.makeCategoryEdit(
|
||||
view.fragmentContext,
|
||||
media,
|
||||
selectedCategories,
|
||||
wikiText,
|
||||
).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
Timber.d("Categories are added.")
|
||||
media.addedCategories = selectedCategories
|
||||
repository.cleanup()
|
||||
view.dismissProgressDialog()
|
||||
view.refreshCategories()
|
||||
view.goBackToPreviousScreen()
|
||||
}, {
|
||||
Timber.e(
|
||||
"Failed to update categories",
|
||||
)
|
||||
}),
|
||||
)
|
||||
} catch (e: InvalidLoginTokenException) {
|
||||
view.navigateToLoginScreen()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repository.cleanup()
|
||||
view.showNoCategorySelected()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* needed for tests
|
||||
*/
|
||||
fun setCategoryListValue(categoryItems: List<CategoryItem>) {
|
||||
categoryList.postValue(categoryItems)
|
||||
}
|
||||
/**
|
||||
* Selects each [CategoryItem] in a given list as if they were clicked by the user by calling
|
||||
* [onCategoryItemClicked] for each category and adding the category to [categoryList]
|
||||
*/
|
||||
private fun selectNewCategories(toSelect: List<CategoryItem>) {
|
||||
toSelect.forEach {
|
||||
it.isSelected = true
|
||||
repository.onCategoryClicked(it, media)
|
||||
}
|
||||
|
||||
override fun selectCategories() {
|
||||
compositeDisposable.add(
|
||||
repository.placeCategories
|
||||
.subscribeOn(ioScheduler)
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(::selectNewCategories),
|
||||
)
|
||||
}
|
||||
}
|
||||
// Add the new selections to the list of category items so that the selections appear
|
||||
// immediately (i.e. without any search term queries)
|
||||
categoryList.value
|
||||
?.toMutableList()
|
||||
?.let { toSelect + it }
|
||||
?.distinctBy(CategoryItem::name)
|
||||
?.let { setCategoryListValue(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Livedata being used to observe category list inside
|
||||
* @see UploadCategoriesFragment
|
||||
* Any changes to category list reflect immediately to the adapter list
|
||||
*/
|
||||
override fun getCategories(): LiveData<List<CategoryItem>> = categoryList
|
||||
|
||||
/**
|
||||
* needed for tests
|
||||
*/
|
||||
fun setCategoryList(categoryList: MutableLiveData<List<CategoryItem>>) {
|
||||
this.categoryList = categoryList
|
||||
}
|
||||
|
||||
/**
|
||||
* needed for tests
|
||||
*/
|
||||
fun setCategoryListValue(categoryItems: List<CategoryItem>) {
|
||||
categoryList.postValue(categoryItems)
|
||||
}
|
||||
|
||||
override fun selectCategories() {
|
||||
compositeDisposable.add(
|
||||
repository.placeCategories
|
||||
.subscribeOn(ioScheduler)
|
||||
.observeOn(mainThreadScheduler)
|
||||
.subscribe(::selectNewCategories),
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue