Skip to content

Commit

Permalink
Load main page content in order asychronously (#140)
Browse files Browse the repository at this point in the history
Replaces #139 

After #131 &
#133, the rows on
the main page load asynchronously. And the first one to load grabs the
focus of the page which is not ideal.

This PR refactors so that all of the data is fetch asynchronously, but
added to the UI in order.

Additionally, in the event of failures, focus will be moved to the top
bar. Before focus would be lost and you have to press up to get to the
bar to scroll over the settings.
  • Loading branch information
damontecres authored Feb 24, 2024
1 parent ee2c621 commit 1eb174b
Showing 1 changed file with 88 additions and 80 deletions.
168 changes: 88 additions & 80 deletions app/src/main/java/com/github/damontecres/stashapp/MainFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import com.github.damontecres.stashapp.util.getInt
import com.github.damontecres.stashapp.util.supportedFilterModes
import com.github.damontecres.stashapp.util.testStashConnection
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.util.Objects

Expand Down Expand Up @@ -140,7 +142,7 @@ class MainFragment : BrowseSupportFragment() {
}
} else {
clearData()
rowsAdapter.clear()
requireActivity().findViewById<View?>(R.id.search_button).requestFocus()
Toast.makeText(requireContext(), "Connection to Stash failed.", Toast.LENGTH_LONG)
.show()
}
Expand Down Expand Up @@ -188,6 +190,7 @@ class MainFragment : BrowseSupportFragment() {
}

private fun clearData() {
rowsAdapter.clear()
adapters.forEach { it.clear() }
}

Expand Down Expand Up @@ -225,7 +228,8 @@ class MainFragment : BrowseSupportFragment() {
"Stash server version ${serverInfo.version.version} is not supported!"
Log.e(TAG, msg)
Toast.makeText(requireContext(), msg, Toast.LENGTH_LONG).show()
rowsAdapter.clear()
clearData()
requireActivity().findViewById<View?>(R.id.search_button).requestFocus()
} else if (serverInfo != null) {
try {
val queryEngine = QueryEngine(requireContext(), showToasts = true)
Expand All @@ -245,33 +249,38 @@ class MainFragment : BrowseSupportFragment() {
val ui = config.ui
val frontPageContent =
(ui as Map<String, *>)["frontPageContent"] as List<Map<String, *>>
frontPageContent.forEachIndexed { index, frontPageFilter ->
val adapter = ArrayObjectAdapter(StashPresenter.SELECTOR)
adapters.add(adapter)

when (val filterType = frontPageFilter["__typename"] as String) {
"CustomFilter" -> {
addCustomFilterRow(
index,
frontPageFilter,
adapter,
queryEngine,
)
}

"SavedFilter" -> {
addSavedFilterRow(
index,
frontPageFilter,
adapter,
queryEngine,
)
}

else -> {
Log.w(TAG, "Unknown frontPageFilter typename: $filterType")
val jobs =
frontPageContent.map { frontPageFilter ->
val adapter = ArrayObjectAdapter(StashPresenter.SELECTOR)
adapters.add(adapter)
when (
val filterType =
frontPageFilter["__typename"] as String
) {
"CustomFilter" -> {
addCustomFilterRow(
frontPageFilter,
adapter,
queryEngine,
)
}

"SavedFilter" -> {
addSavedFilterRow(
frontPageFilter,
adapter,
queryEngine,
)
}

else -> {
Log.w(TAG, "Unknown frontPageFilter typename: $filterType")
null
}
}
}
jobs.forEachIndexed { index, job ->
job?.await()?.let { row -> rowsAdapter.set(index, row) }
}
}
}
Expand All @@ -281,19 +290,20 @@ class MainFragment : BrowseSupportFragment() {
"Stash not configured. Please enter the URL in settings!",
Toast.LENGTH_LONG,
).show()
requireActivity().findViewById<View?>(R.id.search_button).requestFocus()
}
} else {
rowsAdapter.clear()
clearData()
requireActivity().findViewById<View?>(R.id.search_button).requestFocus()
}
}
}

private fun addCustomFilterRow(
index: Int,
frontPageFilter: Map<String, *>,
adapter: ArrayObjectAdapter,
queryEngine: QueryEngine,
) {
): Deferred<ListRow?>? {
val exHandler =
CoroutineExceptionHandler { _, ex ->
Log.e(TAG, "Exception in addCustomFilterRow", ex)
Expand All @@ -320,76 +330,74 @@ class MainFragment : BrowseSupportFragment() {
val mode = FilterMode.safeValueOf(frontPageFilter["mode"] as String)
if (mode !in supportedFilterModes) {
Log.w(TAG, "CustomFilter mode is $mode which is not supported yet")
return
return null
}
rowsAdapter.set(
index,
ListRow(
HeaderItem(description),
adapter,
),
)
viewLifecycleOwner.lifecycleScope.launch(exHandler) {
val direction = frontPageFilter["direction"] as String?
val directionEnum =
if (direction != null) {
val enum = SortDirectionEnum.safeValueOf(direction.uppercase())
if (enum == SortDirectionEnum.UNKNOWN__) {
val job =
viewLifecycleOwner.lifecycleScope.async(exHandler) {
val direction = frontPageFilter["direction"] as String?
val directionEnum =
if (direction != null) {
val enum = SortDirectionEnum.safeValueOf(direction.uppercase())
if (enum == SortDirectionEnum.UNKNOWN__) {
SortDirectionEnum.DESC
}
enum
} else {
SortDirectionEnum.DESC
}
enum
} else {
SortDirectionEnum.DESC
}
val pageSize =
PreferenceManager.getDefaultSharedPreferences(requireContext())
.getInt("maxSearchResults", 25)
val filter =
FindFilterType(
direction = Optional.presentIfNotNull(directionEnum),
sort = Optional.presentIfNotNull(sortBy),
per_page = Optional.present(pageSize),
)

when (mode) {
FilterMode.SCENES -> {
adapter.addAll(0, queryEngine.findScenes(filter))
}
val pageSize =
PreferenceManager.getDefaultSharedPreferences(requireContext())
.getInt("maxSearchResults", 25)
val filter =
FindFilterType(
direction = Optional.presentIfNotNull(directionEnum),
sort = Optional.presentIfNotNull(sortBy),
per_page = Optional.present(pageSize),
)
when (mode) {
FilterMode.SCENES -> {
adapter.addAll(0, queryEngine.findScenes(filter))
}

FilterMode.STUDIOS -> {
adapter.addAll(0, queryEngine.findStudios(filter))
}
FilterMode.STUDIOS -> {
adapter.addAll(0, queryEngine.findStudios(filter))
}

FilterMode.PERFORMERS -> {
adapter.addAll(0, queryEngine.findPerformers(filter))
}
FilterMode.PERFORMERS -> {
adapter.addAll(0, queryEngine.findPerformers(filter))
}

FilterMode.MOVIES -> {
adapter.addAll(0, queryEngine.findMovies(filter))
}
FilterMode.MOVIES -> {
adapter.addAll(0, queryEngine.findMovies(filter))
}

else -> {
Log.w(TAG, "Unsupported mode in frontpage: $mode")
else -> {
Log.w(TAG, "Unsupported mode in frontpage: $mode")
}
}
adapter.add(StashCustomFilter(mode, direction, sortBy, description))
ListRow(
HeaderItem(description),
adapter,
)
}
adapter.add(StashCustomFilter(mode, direction, sortBy, description))
}
return job
} catch (ex: Exception) {
Log.e(TAG, "Exception during addCustomFilterRow", ex)
Toast.makeText(
requireContext(),
"CustomFilter parse error: ${ex.message}",
Toast.LENGTH_LONG,
).show()
return null
}
}

private fun addSavedFilterRow(
index: Int,
frontPageFilter: Map<String, *>,
adapter: ArrayObjectAdapter,
queryEngine: QueryEngine,
) {
): Deferred<ListRow?> {
val exHandler =
CoroutineExceptionHandler { _, ex ->
Log.e(TAG, "Exception in addSavedFilterRow", ex)
Expand All @@ -399,13 +407,11 @@ class MainFragment : BrowseSupportFragment() {
Toast.LENGTH_LONG,
).show()
}
viewLifecycleOwner.lifecycleScope.launch(exHandler) {
return viewLifecycleOwner.lifecycleScope.async(exHandler) {
val filterId = frontPageFilter["savedFilterId"]
val result = queryEngine.getSavedFilter(filterId.toString())

if (result?.mode in supportedFilterModes) {
rowsAdapter.set(index, ListRow(HeaderItem(result?.name ?: ""), adapter))

val pageSize =
PreferenceManager.getDefaultSharedPreferences(requireContext())
.getInt("maxSearchResults", 25)
Expand Down Expand Up @@ -475,8 +481,10 @@ class MainFragment : BrowseSupportFragment() {
filter?.sort?.getOrNull(),
),
)
ListRow(HeaderItem(result?.name ?: ""), adapter)
} else {
Log.w(TAG, "SavedFilter mode is ${result?.mode} which is not supported yet")
null
}
}
}
Expand Down

0 comments on commit 1eb174b

Please sign in to comment.