migrate timeline api calls from Rx Single to suspending functions (#2690)

* migrate timeline api calls from Rx Single to suspending functions

* fix tests
This commit is contained in:
Konrad Pozniak 2022-09-13 19:47:55 +02:00 committed by GitHub
parent 25ba40a7ec
commit 655ce30031
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 106 deletions

View file

@ -22,7 +22,6 @@ import androidx.paging.RemoteMediator
import com.keylesspalace.tusky.components.timeline.util.ifExpected
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.viewdata.AttachmentViewData
import kotlinx.coroutines.rx3.await
import retrofit2.HttpException
@OptIn(ExperimentalPagingApi::class)
@ -39,7 +38,7 @@ class AccountMediaRemoteMediator(
try {
val statusResponse = when (loadType) {
LoadType.REFRESH -> {
api.accountStatuses(viewModel.accountId, onlyMedia = true).await()
api.accountStatuses(viewModel.accountId, onlyMedia = true)
}
LoadType.PREPEND -> {
return MediatorResult.Success(endOfPaginationReached = true)
@ -47,7 +46,7 @@ class AccountMediaRemoteMediator(
LoadType.APPEND -> {
val maxId = state.lastItemOrNull()?.statusId
if (maxId != null) {
api.accountStatuses(viewModel.accountId, maxId = maxId, onlyMedia = true).await()
api.accountStatuses(viewModel.accountId, maxId = maxId, onlyMedia = true)
} else {
return MediatorResult.Success(endOfPaginationReached = false)
}

View file

@ -30,7 +30,6 @@ import com.keylesspalace.tusky.db.TimelineStatusEntity
import com.keylesspalace.tusky.db.TimelineStatusWithAccount
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.network.MastodonApi
import kotlinx.coroutines.rx3.await
import retrofit2.HttpException
@OptIn(ExperimentalPagingApi::class)
@ -71,7 +70,7 @@ class CachedTimelineRemoteMediator(
maxId = cachedTopId,
sinceId = topPlaceholderId, // so already existing placeholders don't get accidentally overwritten
limit = state.config.pageSize
).await()
)
val statuses = statusResponse.body()
if (statusResponse.isSuccessful && statuses != null) {
@ -86,14 +85,14 @@ class CachedTimelineRemoteMediator(
val statusResponse = when (loadType) {
LoadType.REFRESH -> {
api.homeTimeline(sinceId = topPlaceholderId, limit = state.config.pageSize).await()
api.homeTimeline(sinceId = topPlaceholderId, limit = state.config.pageSize)
}
LoadType.PREPEND -> {
return MediatorResult.Success(endOfPaginationReached = true)
}
LoadType.APPEND -> {
val maxId = state.pages.findLast { it.data.isNotEmpty() }?.data?.lastOrNull()?.status?.serverId
api.homeTimeline(maxId = maxId, limit = state.config.pageSize).await()
api.homeTimeline(maxId = maxId, limit = state.config.pageSize)
}
}

View file

@ -50,7 +50,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.rx3.await
import retrofit2.HttpException
import javax.inject.Inject
import kotlin.time.DurationUnit
@ -176,7 +175,7 @@ class CachedTimelineViewModel @Inject constructor(
sinceId = nextPlaceholderId,
limit = LOAD_AT_ONCE
)
}.await()
}
val statuses = response.body()
if (!response.isSuccessful || statuses == null) {

View file

@ -45,7 +45,6 @@ import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.rx3.await
import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException
@ -298,7 +297,7 @@ class NetworkTimelineViewModel @Inject constructor(
Kind.FAVOURITES -> api.favourites(fromId, uptoId, limit)
Kind.BOOKMARKS -> api.bookmarks(fromId, uptoId, limit)
Kind.LIST -> api.listTimeline(id!!, fromId, uptoId, limit)
}.await()
}
}
private fun StatusViewData.Concrete.update() {

View file

@ -85,37 +85,38 @@ interface MastodonApi {
fun getFilters(): Single<List<Filter>>
@GET("api/v1/timelines/home")
fun homeTimeline(
@Throws(Exception::class)
suspend fun homeTimeline(
@Query("max_id") maxId: String? = null,
@Query("since_id") sinceId: String? = null,
@Query("limit") limit: Int? = null
): Single<Response<List<Status>>>
): Response<List<Status>>
@GET("api/v1/timelines/public")
fun publicTimeline(
suspend fun publicTimeline(
@Query("local") local: Boolean? = null,
@Query("max_id") maxId: String? = null,
@Query("since_id") sinceId: String? = null,
@Query("limit") limit: Int? = null
): Single<Response<List<Status>>>
): Response<List<Status>>
@GET("api/v1/timelines/tag/{hashtag}")
fun hashtagTimeline(
suspend fun hashtagTimeline(
@Path("hashtag") hashtag: String,
@Query("any[]") any: List<String>?,
@Query("local") local: Boolean?,
@Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?,
@Query("limit") limit: Int?
): Single<Response<List<Status>>>
): Response<List<Status>>
@GET("api/v1/timelines/list/{listId}")
fun listTimeline(
suspend fun listTimeline(
@Path("listId") listId: String,
@Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?,
@Query("limit") limit: Int?
): Single<Response<List<Status>>>
): Response<List<Status>>
@GET("api/v1/notifications")
fun notifications(
@ -317,7 +318,7 @@ interface MastodonApi {
* @param onlyMedia only return statuses that have media attached
*/
@GET("api/v1/accounts/{id}/statuses")
fun accountStatuses(
suspend fun accountStatuses(
@Path("id") accountId: String,
@Query("max_id") maxId: String? = null,
@Query("since_id") sinceId: String? = null,
@ -325,7 +326,7 @@ interface MastodonApi {
@Query("exclude_replies") excludeReplies: Boolean? = null,
@Query("only_media") onlyMedia: Boolean? = null,
@Query("pinned") pinned: Boolean? = null
): Single<Response<List<Status>>>
): Response<List<Status>>
@GET("api/v1/accounts/{id}/followers")
fun accountFollowers(
@ -419,18 +420,18 @@ interface MastodonApi {
fun unblockDomain(@Field("domain") domain: String): Call<Any>
@GET("api/v1/favourites")
fun favourites(
suspend fun favourites(
@Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?,
@Query("limit") limit: Int?
): Single<Response<List<Status>>>
): Response<List<Status>>
@GET("api/v1/bookmarks")
fun bookmarks(
suspend fun bookmarks(
@Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?,
@Query("limit") limit: Int?
): Single<Response<List<Status>>>
): Response<List<Status>>
@GET("api/v1/follow_requests")
fun followRequests(

View file

@ -17,7 +17,6 @@ import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.db.AppDatabase
import com.keylesspalace.tusky.db.Converters
import com.keylesspalace.tusky.db.TimelineStatusWithAccount
import io.reactivex.rxjava3.core.Single
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import okhttp3.ResponseBody.Companion.toResponseBody
@ -30,6 +29,7 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
import org.robolectric.Shadows.shadowOf
import org.robolectric.annotation.Config
@ -78,7 +78,7 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(anyOrNull(), anyOrNull(), anyOrNull()) } doReturn Single.just(Response.error(500, "".toResponseBody()))
onBlocking { homeTimeline(anyOrNull(), anyOrNull(), anyOrNull()) } doReturn Response.error(500, "".toResponseBody())
},
db = db,
gson = Gson()
@ -98,7 +98,7 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(anyOrNull(), anyOrNull(), anyOrNull()) } doReturn Single.error(IOException())
onBlocking { homeTimeline(anyOrNull(), anyOrNull(), anyOrNull()) } doThrow IOException()
},
db = db,
gson = Gson()
@ -154,24 +154,20 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(limit = 3) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(limit = 3) } doReturn Response.success(
listOf(
mockStatus("8"),
mockStatus("7"),
mockStatus("5")
)
)
)
on { homeTimeline(maxId = "3", limit = 3) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(maxId = "3", limit = 3) } doReturn Response.success(
listOf(
mockStatus("3"),
mockStatus("2"),
mockStatus("1")
)
)
)
},
db = db,
gson = Gson()
@ -222,24 +218,20 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(limit = 20) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(limit = 20) } doReturn Response.success(
listOf(
mockStatus("8"),
mockStatus("7"),
mockStatus("5")
)
)
)
on { homeTimeline(maxId = "3", limit = 20) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(maxId = "3", limit = 20) } doReturn Response.success(
listOf(
mockStatus("3"),
mockStatus("2"),
mockStatus("1")
)
)
)
},
db = db,
gson = Gson()
@ -287,24 +279,20 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(limit = 3) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(limit = 3) } doReturn Response.success(
listOf(
mockStatus("6"),
mockStatus("4"),
mockStatus("3")
)
)
)
on { homeTimeline(maxId = "3", limit = 3) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(maxId = "3", limit = 3) } doReturn Response.success(
listOf(
mockStatus("3"),
mockStatus("2"),
mockStatus("1")
)
)
)
},
db = db,
gson = Gson()
@ -344,15 +332,13 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(limit = 20) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(limit = 20) } doReturn Response.success(
listOf(
mockStatus("5"),
mockStatus("4"),
mockStatus("3")
)
)
)
},
db = db,
gson = Gson()
@ -397,17 +383,14 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(limit = 20) } doReturn Single.just(
Response.success(emptyList())
)
on { homeTimeline(maxId = "3", limit = 20) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(limit = 20) } doReturn Response.success(emptyList())
onBlocking { homeTimeline(maxId = "3", limit = 20) } doReturn Response.success(
listOf(
mockStatus("3"),
mockStatus("1")
)
)
)
},
db = db,
gson = Gson()
@ -452,23 +435,19 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(sinceId = "6", limit = 20) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(sinceId = "6", limit = 20) } doReturn Response.success(
listOf(
mockStatus("9"),
mockStatus("8"),
mockStatus("7")
)
)
)
on { homeTimeline(maxId = "8", sinceId = "6", limit = 20) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(maxId = "8", sinceId = "6", limit = 20) } doReturn Response.success(
listOf(
mockStatus("8"),
mockStatus("7")
)
)
)
},
db = db,
gson = Gson()
@ -515,15 +494,13 @@ class CachedTimelineRemoteMediatorTest {
val remoteMediator = CachedTimelineRemoteMediator(
accountManager = accountManager,
api = mock {
on { homeTimeline(maxId = "5", limit = 20) } doReturn Single.just(
Response.success(
onBlocking { homeTimeline(maxId = "5", limit = 20) } doReturn Response.success(
listOf(
mockStatus("3"),
mockStatus("2"),
mockStatus("1")
)
)
)
},
db = db,
gson = Gson()