Skip to content

Commit

Permalink
refactor: Remove rxjava3 from Mastodon API spec (#128)
Browse files Browse the repository at this point in the history
Remove the rxjava3 `Single` type from the MastodonAPI definition,
replacing with `Response` or `NetworkResult` as appropriate.

Update callsites and tests as appropriate.

This removes the need for `com.squareup.retrofit2:adapter-rxjava3`
  • Loading branch information
nikclayton authored Sep 27, 2023
1 parent 11fecb1 commit c5a5540
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 200 deletions.
70 changes: 29 additions & 41 deletions app/src/main/java/app/pachli/BottomSheetActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,15 @@ import android.view.View
import android.widget.LinearLayout
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import app.pachli.components.account.AccountActivity
import app.pachli.components.viewthread.ViewThreadActivity
import app.pachli.network.MastodonApi
import app.pachli.util.looksLikeMastodonUrl
import app.pachli.util.openLink
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider
import autodispose2.autoDispose
import at.connyduck.calladapter.networkresult.fold
import com.google.android.material.bottomsheet.BottomSheetBehavior
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.launch
import javax.inject.Inject

/** this is the base class for all activities that open links
Expand Down Expand Up @@ -73,41 +72,34 @@ abstract class BottomSheetActivity : BaseActivity() {
return
}

mastodonApi.searchObservable(
query = url,
resolve = true,
).observeOn(AndroidSchedulers.mainThread())
.autoDispose(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))
.subscribe(
{ (accounts, statuses) ->
if (getCancelSearchRequested(url)) {
return@subscribe
}
onBeginSearch(url)

onEndSearch(url)
lifecycleScope.launch {
mastodonApi.search(query = url, resolve = true).fold({ searchResult ->
val (accounts, statuses) = searchResult
if (getCancelSearchRequested(url)) return@fold
onEndSearch(url)

if (statuses.isNotEmpty()) {
viewThread(statuses[0].id, statuses[0].url)
return@subscribe
}
accounts.firstOrNull { it.url.equals(url, ignoreCase = true) }?.let { account ->
// Some servers return (unrelated) accounts for url searches (#2804)
// Verify that the account's url matches the query
viewAccount(account.id)
return@subscribe
}
statuses.firstOrNull()?.let {
viewThread(it.id, it.url)
return@fold
}

performUrlFallbackAction(url, lookupFallbackBehavior)
},
{
if (!getCancelSearchRequested(url)) {
onEndSearch(url)
performUrlFallbackAction(url, lookupFallbackBehavior)
}
},
)
// Some servers return (unrelated) accounts for url searches (#2804)
// Verify that the account's url matches the query
accounts.firstOrNull { it.url.equals(url, ignoreCase = true) }?.let {
viewAccount(it.id)
return@fold
}

onBeginSearch(url)
performUrlFallbackAction(url, lookupFallbackBehavior)
}, {
if (!getCancelSearchRequested(url)) {
onEndSearch(url)
performUrlFallbackAction(url, lookupFallbackBehavior)
}
},)
}
}

open fun viewThread(statusId: String, url: String?) {
Expand Down Expand Up @@ -138,14 +130,10 @@ abstract class BottomSheetActivity : BaseActivity() {
}

@VisibleForTesting
fun getCancelSearchRequested(url: String): Boolean {
return url != searchUrl
}
fun getCancelSearchRequested(url: String) = url != searchUrl

@VisibleForTesting
fun isSearching(): Boolean {
return searchUrl != null
}
fun isSearching() = searchUrl != null

@VisibleForTesting
fun onEndSearch(url: String?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.ConcatAdapter
Expand Down Expand Up @@ -55,12 +54,9 @@ import app.pachli.util.show
import app.pachli.util.viewBinding
import app.pachli.view.EndlessOnScrollListener
import at.connyduck.calladapter.networkresult.fold
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from
import autodispose2.autoDispose
import com.google.android.material.color.MaterialColors
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.launch
import retrofit2.Response
import java.io.IOException
Expand Down Expand Up @@ -258,13 +254,12 @@ class AccountListFragment :
accountId: String,
position: Int,
) {
if (accept) {
api.authorizeFollowRequest(accountId)
} else {
api.rejectFollowRequest(accountId)
}.observeOn(AndroidSchedulers.mainThread())
.autoDispose(from(this, Lifecycle.Event.ON_DESTROY))
.subscribe(
viewLifecycleOwner.lifecycleScope.launch {
if (accept) {
api.authorizeFollowRequest(accountId)
} else {
api.rejectFollowRequest(accountId)
}.fold(
{
onRespondToFollowRequestSuccess(position)
},
Expand All @@ -277,6 +272,7 @@ class AccountListFragment :
Log.e(TAG, "Failed to $verb account id $accountId.", throwable)
},
)
}
}

private fun onRespondToFollowRequestSuccess(position: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
Expand All @@ -20,11 +19,8 @@ import app.pachli.util.show
import app.pachli.util.viewBinding
import app.pachli.view.EndlessOnScrollListener
import at.connyduck.calladapter.networkresult.fold
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from
import autodispose2.autoDispose
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand Down Expand Up @@ -101,23 +97,16 @@ class InstanceListFragment :
binding.recyclerView.post { adapter.bottomLoading = true }
}

api.domainBlocks(id, bottomId)
.observeOn(AndroidSchedulers.mainThread())
.autoDispose(from(this, Lifecycle.Event.ON_DESTROY))
.subscribe(
{ response ->
val instances = response.body()

if (response.isSuccessful && instances != null) {
onFetchInstancesSuccess(instances, response.headers()["Link"])
} else {
onFetchInstancesFailure(Exception(response.message()))
}
},
{ throwable ->
onFetchInstancesFailure(throwable)
},
)
viewLifecycleOwner.lifecycleScope.launch {
val response = api.domainBlocks(id, bottomId)
val instances = response.body()

if (response.isSuccessful && instances != null) {
onFetchInstancesSuccess(instances, response.headers()["Link"])
} else {
onFetchInstancesFailure(Exception(response.message()))
}
}
}

private fun onFetchInstancesSuccess(instances: List<String>, linkHeader: String?) {
Expand Down Expand Up @@ -158,6 +147,6 @@ class InstanceListFragment :
}

companion object {
private const val TAG = "InstanceList" // logging tag
private const val TAG = "InstanceList"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.rx3.await
import retrofit2.HttpException
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
Expand Down Expand Up @@ -436,9 +435,9 @@ class NotificationsViewModel @Inject constructor(
try {
when (action) {
is NotificationAction.AcceptFollowRequest ->
timelineCases.acceptFollowRequest(action.accountId).await()
timelineCases.acceptFollowRequest(action.accountId)
is NotificationAction.RejectFollowRequest ->
timelineCases.rejectFollowRequest(action.accountId).await()
timelineCases.rejectFollowRequest(action.accountId)
}
uiSuccess.emit(NotificationActionSuccess.from(action))
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ class StatusesPagingSource(
withContext(Dispatchers.IO) {
val initialStatus = async { getSingleStatus(key) }
val additionalStatuses = async { getStatusList(maxId = key, limit = params.loadSize - 1) }
listOf(initialStatus.await()) + additionalStatuses.await()
buildList {
initialStatus.await()?.let { this.add(it) }
additionalStatuses.await()?.let { this.addAll(it) }
}
}
} else {
val maxId = if (params is LoadParams.Refresh || params is LoadParams.Append) {
Expand All @@ -58,7 +61,7 @@ class StatusesPagingSource(
null
}

getStatusList(minId = minId, maxId = maxId, limit = params.loadSize)
getStatusList(minId = minId, maxId = maxId, limit = params.loadSize) ?: emptyList()
}
return LoadResult.Page(
data = result,
Expand All @@ -71,18 +74,18 @@ class StatusesPagingSource(
}
}

private suspend fun getSingleStatus(statusId: String): Status {
return mastodonApi.statusObservable(statusId).await()
private suspend fun getSingleStatus(statusId: String): Status? {
return mastodonApi.status(statusId).getOrNull()
}

private suspend fun getStatusList(minId: String? = null, maxId: String? = null, limit: Int): List<Status> {
return mastodonApi.accountStatusesObservable(
private suspend fun getStatusList(minId: String? = null, maxId: String? = null, limit: Int): List<Status>? {
return mastodonApi.accountStatuses(
accountId = accountId,
maxId = maxId,
sinceId = null,
minId = minId,
limit = limit,
excludeReblogs = true,
).await()
).body()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,11 @@

package app.pachli.components.scheduled

import android.util.Log
import androidx.paging.PagingSource
import androidx.paging.PagingState
import app.pachli.entity.ScheduledStatus
import app.pachli.network.MastodonApi
import kotlinx.coroutines.rx3.await
import at.connyduck.calladapter.networkresult.getOrElse

class ScheduledStatusPagingSourceFactory(
private val mastodonApi: MastodonApi,
Expand Down Expand Up @@ -59,21 +58,12 @@ class ScheduledStatusPagingSource(
nextKey = scheduledStatusesCache.lastOrNull()?.id,
)
} else {
try {
val result = mastodonApi.scheduledStatuses(
maxId = params.key,
limit = params.loadSize,
).await()
val result = mastodonApi.scheduledStatuses(
maxId = params.key,
limit = params.loadSize,
).getOrElse { return LoadResult.Error(it) }

LoadResult.Page(
data = result,
prevKey = null,
nextKey = result.lastOrNull()?.id,
)
} catch (e: Exception) {
Log.w("ScheduledStatuses", "Error loading scheduled statuses", e)
LoadResult.Error(e)
}
LoadResult.Page(data = result, prevKey = null, nextKey = result.lastOrNull()?.id)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import androidx.paging.PagingState
import app.pachli.components.search.SearchType
import app.pachli.entity.SearchResult
import app.pachli.network.MastodonApi
import kotlinx.coroutines.rx3.await
import at.connyduck.calladapter.networkresult.getOrElse

class SearchPagingSource<T : Any>(
private val mastodonApi: MastodonApi,
Expand Down Expand Up @@ -53,31 +53,27 @@ class SearchPagingSource<T : Any>(

val currentKey = params.key ?: 0

try {
val data = mastodonApi.searchObservable(
query = searchRequest,
type = searchType.apiParameter,
resolve = true,
limit = params.loadSize,
offset = currentKey,
following = false,
).await()
val data = mastodonApi.search(
query = searchRequest,
type = searchType.apiParameter,
resolve = true,
limit = params.loadSize,
offset = currentKey,
following = false,
).getOrElse { return LoadResult.Error(it) }

val res = parser(data)
val res = parser(data)

val nextKey = if (res.isEmpty()) {
null
} else {
currentKey + res.size
}

return LoadResult.Page(
data = res,
prevKey = null,
nextKey = nextKey,
)
} catch (e: Exception) {
return LoadResult.Error(e)
val nextKey = if (res.isEmpty()) {
null
} else {
currentKey + res.size
}

return LoadResult.Page(
data = res,
prevKey = null,
nextKey = nextKey,
)
}
}
2 changes: 0 additions & 2 deletions app/src/main/java/app/pachli/di/NetworkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import okhttp3.OkHttp
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create
import java.net.IDN
Expand Down Expand Up @@ -115,7 +114,6 @@ class NetworkModule {
return Retrofit.Builder().baseUrl("https://" + MastodonApi.PLACEHOLDER_DOMAIN)
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addCallAdapterFactory(NetworkResultCallAdapterFactory.create())
.build()
}
Expand Down
Loading

0 comments on commit c5a5540

Please sign in to comment.