get rid of BaseFragment by using RxJava instead of Retrofit Calls (#2055)

* get rid of BaseFragment by using RxJava instead of Retrofit Calls

* fix tests
This commit is contained in:
Konrad Pozniak 2021-01-31 19:34:33 +01:00 committed by GitHub
parent 2d2b79aa47
commit 886ff2f06b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 170 additions and 309 deletions

View File

@ -57,7 +57,7 @@ class DraftsViewModel @Inject constructor(
} }
fun getToot(tootId: String): Single<Status> { fun getToot(tootId: String): Single<Status> {
return api.statusSingle(tootId) return api.status(tootId)
} }
override fun onCleared() { override fun onCleared() {

View File

@ -5,6 +5,7 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -14,7 +15,6 @@ import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.components.instancemute.adapter.DomainMutesAdapter import com.keylesspalace.tusky.components.instancemute.adapter.DomainMutesAdapter
import com.keylesspalace.tusky.components.instancemute.interfaces.InstanceActionListener import com.keylesspalace.tusky.components.instancemute.interfaces.InstanceActionListener
import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.fragment.BaseFragment
import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.HttpHeaderLink import com.keylesspalace.tusky.util.HttpHeaderLink
import com.keylesspalace.tusky.util.hide import com.keylesspalace.tusky.util.hide
@ -30,7 +30,7 @@ import retrofit2.Response
import java.io.IOException import java.io.IOException
import javax.inject.Inject import javax.inject.Inject
class InstanceListFragment: BaseFragment(), Injectable, InstanceActionListener { class InstanceListFragment: Fragment(R.layout.fragment_instance_list), Injectable, InstanceActionListener {
@Inject @Inject
lateinit var api: MastodonApi lateinit var api: MastodonApi
@ -39,10 +39,6 @@ class InstanceListFragment: BaseFragment(), Injectable, InstanceActionListener {
private var adapter = DomainMutesAdapter(this) private var adapter = DomainMutesAdapter(this)
private lateinit var scrollListener: EndlessOnScrollListener private lateinit var scrollListener: EndlessOnScrollListener
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_instance_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View File

@ -17,9 +17,8 @@ package com.keylesspalace.tusky.fragment
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -45,14 +44,12 @@ import com.uber.autodispose.autoDispose
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_account_list.* import kotlinx.android.synthetic.main.fragment_account_list.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class AccountListFragment : BaseFragment(), AccountActionListener, Injectable { class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountActionListener, Injectable {
@Inject @Inject
lateinit var api: MastodonApi lateinit var api: MastodonApi
@ -71,10 +68,6 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
id = arguments?.getString(ARG_ID) id = arguments?.getString(ARG_ID)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.fragment_account_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -202,27 +195,23 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
override fun onRespondToFollowRequest(accept: Boolean, accountId: String, override fun onRespondToFollowRequest(accept: Boolean, accountId: String,
position: Int) { position: Int) {
val callback = object : Callback<Relationship> { if (accept) {
override fun onResponse(call: Call<Relationship>, response: Response<Relationship>) {
if (response.isSuccessful) {
onRespondToFollowRequestSuccess(position)
} else {
onRespondToFollowRequestFailure(accept, accountId)
}
}
override fun onFailure(call: Call<Relationship>, t: Throwable) {
onRespondToFollowRequestFailure(accept, accountId)
}
}
val call = if (accept) {
api.authorizeFollowRequest(accountId) api.authorizeFollowRequest(accountId)
} else { } else {
api.rejectFollowRequest(accountId) api.rejectFollowRequest(accountId)
} }.observeOn(AndroidSchedulers.mainThread())
callList.add(call) .autoDispose(from(this, Lifecycle.Event.ON_DESTROY))
call.enqueue(callback) .subscribe({
onRespondToFollowRequestSuccess(position)
}, { throwable ->
val verb = if (accept) {
"accept"
} else {
"reject"
}
Log.e(TAG, "Failed to $verb account id $accountId.", throwable)
})
} }
private fun onRespondToFollowRequestSuccess(position: Int) { private fun onRespondToFollowRequestSuccess(position: Int) {
@ -230,15 +219,6 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
followRequestsAdapter.removeItem(position) followRequestsAdapter.removeItem(position)
} }
private fun onRespondToFollowRequestFailure(accept: Boolean, accountId: String) {
val verb = if (accept) {
"accept"
} else {
"reject"
}
Log.e(TAG, "Failed to $verb account id $accountId.")
}
private fun getFetchCallByListType(fromId: String?): Single<Response<List<Account>>> { private fun getFetchCallByListType(fromId: String?): Single<Response<List<Account>>> {
return when (type) { return when (type) {
Type.FOLLOWS -> { Type.FOLLOWS -> {

View File

@ -18,12 +18,13 @@ package com.keylesspalace.tusky.fragment
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@ -40,9 +41,11 @@ import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.view.SquareImageView import com.keylesspalace.tusky.view.SquareImageView
import com.keylesspalace.tusky.viewdata.AttachmentViewData import com.keylesspalace.tusky.viewdata.AttachmentViewData
import com.uber.autodispose.android.lifecycle.autoDispose
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.fragment_timeline.* import kotlinx.android.synthetic.main.fragment_timeline.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
@ -54,7 +57,7 @@ import javax.inject.Inject
* Fragment with multiple columns of media previews for the specified account. * Fragment with multiple columns of media previews for the specified account.
*/ */
class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable { class AccountMediaFragment : Fragment(R.layout.fragment_timeline), RefreshableFragment, Injectable {
companion object { companion object {
@JvmStatic @JvmStatic
fun newInstance(accountId: String, enableSwipeToRefresh:Boolean=true): AccountMediaFragment { fun newInstance(accountId: String, enableSwipeToRefresh:Boolean=true): AccountMediaFragment {
@ -78,14 +81,13 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
lateinit var api: MastodonApi lateinit var api: MastodonApi
private val adapter = MediaGridAdapter() private val adapter = MediaGridAdapter()
private var currentCall: Call<List<Status>>? = null
private val statuses = mutableListOf<Status>() private val statuses = mutableListOf<Status>()
private var fetchingStatus = FetchingStatus.NOT_FETCHING private var fetchingStatus = FetchingStatus.NOT_FETCHING
private lateinit var accountId: String private lateinit var accountId: String
private val callback = object : Callback<List<Status>> { private val callback = object : SingleObserver<Response<List<Status>>> {
override fun onFailure(call: Call<List<Status>>?, t: Throwable?) { override fun onError(t: Throwable) {
fetchingStatus = FetchingStatus.NOT_FETCHING fetchingStatus = FetchingStatus.NOT_FETCHING
if (isAdded) { if (isAdded) {
@ -107,7 +109,7 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
Log.d(TAG, "Failed to fetch account media", t) Log.d(TAG, "Failed to fetch account media", t)
} }
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) { override fun onSuccess(response: Response<List<Status>>) {
fetchingStatus = FetchingStatus.NOT_FETCHING fetchingStatus = FetchingStatus.NOT_FETCHING
if (isAdded) { if (isAdded) {
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
@ -128,22 +130,23 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
if (statuses.isEmpty()) { if (statuses.isEmpty()) {
statusView.show() statusView.show()
statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty)
null)
} }
} }
} }
} }
override fun onSubscribe(d: Disposable) {}
} }
private val bottomCallback = object : Callback<List<Status>> { private val bottomCallback = object : SingleObserver<Response<List<Status>>> {
override fun onFailure(call: Call<List<Status>>?, t: Throwable?) { override fun onError(t: Throwable) {
fetchingStatus = FetchingStatus.NOT_FETCHING fetchingStatus = FetchingStatus.NOT_FETCHING
Log.d(TAG, "Failed to fetch account media", t) Log.d(TAG, "Failed to fetch account media", t)
} }
override fun onResponse(call: Call<List<Status>>, response: Response<List<Status>>) { override fun onSuccess(response: Response<List<Status>>) {
fetchingStatus = FetchingStatus.NOT_FETCHING fetchingStatus = FetchingStatus.NOT_FETCHING
val body = response.body() val body = response.body()
body?.let { fetched -> body?.let { fetched ->
@ -160,6 +163,7 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
} }
} }
override fun onSubscribe(d: Disposable) { }
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -167,10 +171,6 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
isSwipeToRefreshEnabled = arguments?.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH,true) == true isSwipeToRefreshEnabled = arguments?.getBoolean(ARG_ENABLE_SWIPE_TO_REFRESH,true) == true
accountId = arguments?.getString(ACCOUNT_ID_ARG)!! accountId = arguments?.getString(ACCOUNT_ID_ARG)!!
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_timeline, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -202,8 +202,10 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
statuses.lastOrNull()?.let { (id) -> statuses.lastOrNull()?.let { (id) ->
Log.d(TAG, "Requesting statuses with max_id: ${id}, (bottom)") Log.d(TAG, "Requesting statuses with max_id: ${id}, (bottom)")
fetchingStatus = FetchingStatus.FETCHING_BOTTOM fetchingStatus = FetchingStatus.FETCHING_BOTTOM
currentCall = api.accountStatuses(accountId, id, null, null, null, true, null) api.accountStatuses(accountId, id, null, null, null, true, null)
currentCall?.enqueue(bottomCallback) .observeOn(AndroidSchedulers.mainThread())
.autoDispose(this@AccountMediaFragment, Lifecycle.Event.ON_DESTROY)
.subscribe(bottomCallback)
} }
} }
} }
@ -216,14 +218,15 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
private fun refresh() { private fun refresh() {
statusView.hide() statusView.hide()
if (fetchingStatus != FetchingStatus.NOT_FETCHING) return if (fetchingStatus != FetchingStatus.NOT_FETCHING) return
currentCall = if (statuses.isEmpty()) { if (statuses.isEmpty()) {
fetchingStatus = FetchingStatus.INITIAL_FETCHING fetchingStatus = FetchingStatus.INITIAL_FETCHING
api.accountStatuses(accountId, null, null, null, null, true, null) api.accountStatuses(accountId, null, null, null, null, true, null)
} else { } else {
fetchingStatus = FetchingStatus.REFRESHING fetchingStatus = FetchingStatus.REFRESHING
api.accountStatuses(accountId, null, statuses[0].id, null, null, true, null) api.accountStatuses(accountId, null, statuses[0].id, null, null, true, null)
} }.observeOn(AndroidSchedulers.mainThread())
currentCall?.enqueue(callback) .autoDispose(this, Lifecycle.Event.ON_DESTROY)
.subscribe(callback)
if (!isSwipeToRefreshEnabled) if (!isSwipeToRefreshEnabled)
topProgressBar?.show() topProgressBar?.show()
@ -235,8 +238,10 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
} }
if (fetchingStatus == FetchingStatus.NOT_FETCHING && statuses.isEmpty()) { if (fetchingStatus == FetchingStatus.NOT_FETCHING && statuses.isEmpty()) {
fetchingStatus = FetchingStatus.INITIAL_FETCHING fetchingStatus = FetchingStatus.INITIAL_FETCHING
currentCall = api.accountStatuses(accountId, null, null, null, null, true, null) api.accountStatuses(accountId, null, null, null, null, true, null)
currentCall?.enqueue(callback) .observeOn(AndroidSchedulers.mainThread())
.autoDispose(this@AccountMediaFragment, Lifecycle.Event.ON_DESTROY)
.subscribe(callback)
} }
else if (needToRefresh) else if (needToRefresh)
refresh() refresh()
@ -339,5 +344,4 @@ class AccountMediaFragment : BaseFragment(), RefreshableFragment, Injectable {
needToRefresh = true needToRefresh = true
} }
} }

View File

@ -1,43 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* 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 <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.fragment;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
public class BaseFragment extends Fragment {
protected List<Call> callList;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
callList = new ArrayList<>();
}
@Override
public void onDestroy() {
for (Call call : callList) {
call.cancel();
}
super.onDestroy();
}
}

View File

@ -102,13 +102,11 @@ import at.connyduck.sparkbutton.helpers.Utils;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import kotlin.Unit; import kotlin.Unit;
import kotlin.collections.CollectionsKt; import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function1;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import static com.keylesspalace.tusky.util.StringUtils.isLessThan; import static com.keylesspalace.tusky.util.StringUtils.isLessThan;
import static com.uber.autodispose.AutoDispose.autoDisposable; import static com.uber.autodispose.AutoDispose.autoDisposable;
@ -125,8 +123,9 @@ public class NotificationsFragment extends SFragment implements
private static final int LOAD_AT_ONCE = 30; private static final int LOAD_AT_ONCE = 30;
private int maxPlaceholderId = 0; private int maxPlaceholderId = 0;
private final Set<Notification.Type> notificationFilter = new HashSet<>();
private Set<Notification.Type> notificationFilter = new HashSet<>(); private final CompositeDisposable disposables = new CompositeDisposable();
private enum FetchEnd { private enum FetchEnd {
TOP, TOP,
@ -685,32 +684,21 @@ public class NotificationsFragment extends SFragment implements
updateAdapter(); updateAdapter();
//Execute clear notifications request //Execute clear notifications request
Call<ResponseBody> call = mastodonApi.clearNotifications(); mastodonApi.clearNotifications()
call.enqueue(new Callback<ResponseBody>() { .observeOn(AndroidSchedulers.mainThread())
@Override .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) { .subscribe(
if (isAdded()) { response -> {
if (!response.isSuccessful()) { // nothing to do
//Reload notifications on failure },
fullyRefreshWithProgressBar(true); throwable -> {
} //Reload notifications on failure
} fullyRefreshWithProgressBar(true);
} });
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
//Reload notifications on failure
fullyRefreshWithProgressBar(true);
}
});
callList.add(call);
} }
private void resetNotificationsLoad() { private void resetNotificationsLoad() {
for (Call callItem : callList) { disposables.clear();
callItem.cancel();
}
callList.clear();
bottomLoading = false; bottomLoading = false;
topLoading = false; topLoading = false;
@ -840,8 +828,8 @@ public class NotificationsFragment extends SFragment implements
@Override @Override
public void onRespondToFollowRequest(boolean accept, String id, int position) { public void onRespondToFollowRequest(boolean accept, String id, int position) {
Single<Relationship> request = accept ? Single<Relationship> request = accept ?
mastodonApi.authorizeFollowRequestObservable(id) : mastodonApi.authorizeFollowRequest(id) :
mastodonApi.rejectFollowRequestObservable(id); mastodonApi.rejectFollowRequest(id);
request.observeOn(AndroidSchedulers.mainThread()) request.observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe( .subscribe(
@ -959,27 +947,20 @@ public class NotificationsFragment extends SFragment implements
bottomLoading = true; bottomLoading = true;
} }
Call<List<Notification>> call = mastodonApi.notifications(fromId, uptoId, LOAD_AT_ONCE, showNotificationsFilter ? notificationFilter : null); Disposable notificationCall = mastodonApi.notifications(fromId, uptoId, LOAD_AT_ONCE, showNotificationsFilter ? notificationFilter : null)
.observeOn(AndroidSchedulers.mainThread())
call.enqueue(new Callback<List<Notification>>() { .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
@Override .subscribe(
public void onResponse(@NonNull Call<List<Notification>> call, response -> {
@NonNull Response<List<Notification>> response) { if (response.isSuccessful()) {
if (response.isSuccessful()) { String linkHeader = response.headers().get("Link");
String linkHeader = response.headers().get("Link"); onFetchNotificationsSuccess(response.body(), linkHeader, fetchEnd, pos);
onFetchNotificationsSuccess(response.body(), linkHeader, fetchEnd, pos); } else {
} else { onFetchNotificationsFailure(new Exception(response.message()), fetchEnd, pos);
onFetchNotificationsFailure(new Exception(response.message()), fetchEnd, pos); }
} },
} throwable -> onFetchNotificationsFailure(throwable, fetchEnd, pos));
disposables.add(notificationCall);
@Override
public void onFailure(@NonNull Call<List<Notification>> call, @NonNull Throwable t) {
if (!call.isCanceled())
onFetchNotificationsFailure((Exception) t, fetchEnd, pos);
}
});
callList.add(call);
} }
private void onFetchNotificationsSuccess(List<Notification> notifications, String linkHeader, private void onFetchNotificationsSuccess(List<Notification> notifications, String linkHeader,
@ -1038,7 +1019,7 @@ public class NotificationsFragment extends SFragment implements
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
} }
private void onFetchNotificationsFailure(Exception exception, FetchEnd fetchEnd, int position) { private void onFetchNotificationsFailure(Throwable throwable, FetchEnd fetchEnd, int position) {
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
if (fetchEnd == FetchEnd.MIDDLE && !notifications.get(position).isRight()) { if (fetchEnd == FetchEnd.MIDDLE && !notifications.get(position).isRight()) {
Placeholder placeholder = notifications.get(position).asLeft(); Placeholder placeholder = notifications.get(position).asLeft();
@ -1050,7 +1031,7 @@ public class NotificationsFragment extends SFragment implements
this.statusView.setVisibility(View.VISIBLE); this.statusView.setVisibility(View.VISIBLE);
swipeRefreshLayout.setEnabled(false); swipeRefreshLayout.setEnabled(false);
this.showingError = true; this.showingError = true;
if (exception instanceof IOException) { if (throwable instanceof IOException) {
this.statusView.setup(R.drawable.elephant_offline, R.string.error_network, __ -> { this.statusView.setup(R.drawable.elephant_offline, R.string.error_network, __ -> {
this.progressBar.setVisibility(View.VISIBLE); this.progressBar.setVisibility(View.VISIBLE);
this.onRefresh(); this.onRefresh();
@ -1065,7 +1046,7 @@ public class NotificationsFragment extends SFragment implements
} }
updateFilterVisibility(); updateFilterVisibility();
} }
Log.e(TAG, "Fetch failure: " + exception.getMessage()); Log.e(TAG, "Fetch failure: " + throwable.getMessage());
if (fetchEnd == FetchEnd.TOP) { if (fetchEnd == FetchEnd.TOP) {
topLoading = false; topLoading = false;

View File

@ -20,7 +20,6 @@ import android.app.DownloadManager;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
@ -30,8 +29,6 @@ import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -41,14 +38,14 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu; import androidx.appcompat.widget.PopupMenu;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceManager;
import com.keylesspalace.tusky.BaseActivity; import com.keylesspalace.tusky.BaseActivity;
import com.keylesspalace.tusky.BottomSheetActivity; import com.keylesspalace.tusky.BottomSheetActivity;
import com.keylesspalace.tusky.MainActivity; import com.keylesspalace.tusky.MainActivity;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.PostLookupFallbackBehavior; import com.keylesspalace.tusky.PostLookupFallbackBehavior;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.ViewMediaActivity; import com.keylesspalace.tusky.ViewMediaActivity;
import com.keylesspalace.tusky.ViewTagActivity; import com.keylesspalace.tusky.ViewTagActivity;
import com.keylesspalace.tusky.components.compose.ComposeActivity; import com.keylesspalace.tusky.components.compose.ComposeActivity;
@ -76,9 +73,8 @@ import java.util.regex.Pattern;
import javax.inject.Inject; import javax.inject.Inject;
import kotlin.Unit;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import kotlin.Unit;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
@ -92,7 +88,7 @@ import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvid
* adapters. I feel like the profile pages and thread viewer, which I haven't made yet, will also * adapters. I feel like the profile pages and thread viewer, which I haven't made yet, will also
* overlap functionality. So, I'm momentarily leaving it and hopefully working on those will clear * overlap functionality. So, I'm momentarily leaving it and hopefully working on those will clear
* up what needs to be where. */ * up what needs to be where. */
public abstract class SFragment extends BaseFragment implements Injectable { public abstract class SFragment extends Fragment implements Injectable {
protected abstract void removeItem(int position); protected abstract void removeItem(int position);
@ -103,7 +99,7 @@ public abstract class SFragment extends BaseFragment implements Injectable {
private static List<Filter> filters; private static List<Filter> filters;
private boolean filterRemoveRegex; private boolean filterRemoveRegex;
private Matcher filterRemoveRegexMatcher; private Matcher filterRemoveRegexMatcher;
private static Matcher alphanumeric = Pattern.compile("^\\w+$").matcher(""); private static final Matcher alphanumeric = Pattern.compile("^\\w+$").matcher("");
@Inject @Inject
public MastodonApi mastodonApi; public MastodonApi mastodonApi;

View File

@ -101,12 +101,11 @@ import javax.inject.Inject;
import at.connyduck.sparkbutton.helpers.Utils; import at.connyduck.sparkbutton.helpers.Utils;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import kotlin.Unit; import kotlin.Unit;
import kotlin.collections.CollectionsKt; import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function1;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
import static com.uber.autodispose.AutoDispose.autoDisposable; import static com.uber.autodispose.AutoDispose.autoDisposable;
@ -1004,7 +1003,7 @@ public class TimelineFragment extends SFragment implements
} }
} }
private Call<List<Status>> getFetchCallByTimelineType(String fromId, String uptoId) { private Single<Response<List<Status>>> getFetchCallByTimelineType(String fromId, String uptoId) {
MastodonApi api = mastodonApi; MastodonApi api = mastodonApi;
switch (kind) { switch (kind) {
default: default:
@ -1051,37 +1050,31 @@ public class TimelineFragment extends SFragment implements
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY))) .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
.subscribe( .subscribe(
(result) -> onFetchTimelineSuccess(result, fetchEnd, pos), result -> onFetchTimelineSuccess(result, fetchEnd, pos),
(err) -> onFetchTimelineFailure(new Exception(err), fetchEnd, pos) err -> onFetchTimelineFailure(err, fetchEnd, pos)
); );
} else { } else {
Callback<List<Status>> callback = new Callback<List<Status>>() { getFetchCallByTimelineType(maxId, sinceId)
@Override .observeOn(AndroidSchedulers.mainThread())
public void onResponse(@NonNull Call<List<Status>> call, @NonNull Response<List<Status>> response) { .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
if (response.isSuccessful()) { .subscribe(
@Nullable response -> {
String newNextId = extractNextId(response); if (response.isSuccessful()) {
if (newNextId != null) { @Nullable
// when we reach the bottom of the list, we won't have a new link. If String newNextId = extractNextId(response);
// we blindly write `null` here we will start loading from the top if (newNextId != null) {
// again. // when we reach the bottom of the list, we won't have a new link. If
nextId = newNextId; // we blindly write `null` here we will start loading from the top
} // again.
onFetchTimelineSuccess(liftStatusList(response.body()), fetchEnd, pos); nextId = newNextId;
} else { }
onFetchTimelineFailure(new Exception(response.message()), fetchEnd, pos); onFetchTimelineSuccess(liftStatusList(response.body()), fetchEnd, pos);
} } else {
} onFetchTimelineFailure(new Exception(response.message()), fetchEnd, pos);
}
@Override },
public void onFailure(@NonNull Call<List<Status>> call, @NonNull Throwable t) { err -> onFetchTimelineFailure(err, fetchEnd, pos)
onFetchTimelineFailure((Exception) t, fetchEnd, pos); );
}
};
Call<List<Status>> listCall = getFetchCallByTimelineType(maxId, sinceId);
callList.add(listCall);
listCall.enqueue(callback);
} }
} }
@ -1158,7 +1151,7 @@ public class TimelineFragment extends SFragment implements
} }
} }
private void onFetchTimelineFailure(Exception exception, FetchEnd fetchEnd, int position) { private void onFetchTimelineFailure(Throwable throwable, FetchEnd fetchEnd, int position) {
if (isAdded()) { if (isAdded()) {
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
topProgressBar.hide(); topProgressBar.hide();
@ -1177,7 +1170,7 @@ public class TimelineFragment extends SFragment implements
} else if (this.statuses.isEmpty()) { } else if (this.statuses.isEmpty()) {
swipeRefreshLayout.setEnabled(false); swipeRefreshLayout.setEnabled(false);
this.statusView.setVisibility(View.VISIBLE); this.statusView.setVisibility(View.VISIBLE);
if (exception instanceof IOException) { if (throwable instanceof IOException) {
this.statusView.setup(R.drawable.elephant_offline, R.string.error_network, __ -> { this.statusView.setup(R.drawable.elephant_offline, R.string.error_network, __ -> {
this.progressBar.setVisibility(View.VISIBLE); this.progressBar.setVisibility(View.VISIBLE);
this.onRefresh(); this.onRefresh();
@ -1192,7 +1185,7 @@ public class TimelineFragment extends SFragment implements
} }
} }
Log.e(TAG, "Fetch Failure: " + exception.getMessage()); Log.e(TAG, "Fetch Failure: " + throwable.getMessage());
updateBottomLoadingState(fetchEnd); updateBottomLoadingState(fetchEnd);
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
} }

View File

@ -17,10 +17,11 @@ package com.keylesspalace.tusky.fragment
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import androidx.fragment.app.Fragment
import com.keylesspalace.tusky.ViewMediaActivity import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.entity.Attachment
abstract class ViewMediaFragment : BaseFragment() { abstract class ViewMediaFragment : Fragment() {
private var toolbarVisibiltyDisposable: Function0<Boolean>? = null private var toolbarVisibiltyDisposable: Function0<Boolean>? = null
abstract fun setupMediaView( abstract fun setupMediaView(

View File

@ -55,7 +55,6 @@ import com.keylesspalace.tusky.di.Injectable;
import com.keylesspalace.tusky.entity.Filter; import com.keylesspalace.tusky.entity.Filter;
import com.keylesspalace.tusky.entity.Poll; import com.keylesspalace.tusky.entity.Poll;
import com.keylesspalace.tusky.entity.Status; import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.entity.StatusContext;
import com.keylesspalace.tusky.interfaces.StatusActionListener; import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.network.MastodonApi; import com.keylesspalace.tusky.network.MastodonApi;
import com.keylesspalace.tusky.settings.PrefKeys; import com.keylesspalace.tusky.settings.PrefKeys;
@ -75,9 +74,6 @@ import java.util.Locale;
import javax.inject.Inject; import javax.inject.Inject;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import static com.uber.autodispose.AutoDispose.autoDisposable; import static com.uber.autodispose.AutoDispose.autoDisposable;
import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from; import static com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider.from;
@ -463,49 +459,32 @@ public final class ViewThreadFragment extends SFragment implements
} }
private void sendStatusRequest(final String id) { private void sendStatusRequest(final String id) {
Call<Status> call = mastodonApi.status(id); mastodonApi.status(id)
call.enqueue(new Callback<Status>() { .observeOn(AndroidSchedulers.mainThread())
@Override .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
public void onResponse(@NonNull Call<Status> call, @NonNull Response<Status> response) { .subscribe(
if (response.isSuccessful()) { status -> {
int position = setStatus(response.body()); int position = setStatus(status);
recyclerView.scrollToPosition(position); recyclerView.scrollToPosition(position);
} else { },
onThreadRequestFailure(id); throwable -> onThreadRequestFailure(id, throwable)
} );
}
@Override
public void onFailure(@NonNull Call<Status> call, @NonNull Throwable t) {
onThreadRequestFailure(id);
}
});
callList.add(call);
} }
private void sendThreadRequest(final String id) { private void sendThreadRequest(final String id) {
Call<StatusContext> call = mastodonApi.statusContext(id); mastodonApi.statusContext(id)
call.enqueue(new Callback<StatusContext>() { .observeOn(AndroidSchedulers.mainThread())
@Override .as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
public void onResponse(@NonNull Call<StatusContext> call, @NonNull Response<StatusContext> response) { .subscribe(
StatusContext context = response.body(); context -> {
if (response.isSuccessful() && context != null) { swipeRefreshLayout.setRefreshing(false);
swipeRefreshLayout.setRefreshing(false); setContext(context.getAncestors(), context.getDescendants());
setContext(context.getAncestors(), context.getDescendants()); },
} else { throwable -> onThreadRequestFailure(id, throwable)
onThreadRequestFailure(id); );
}
}
@Override
public void onFailure(@NonNull Call<StatusContext> call, @NonNull Throwable t) {
onThreadRequestFailure(id);
}
});
callList.add(call);
} }
private void onThreadRequestFailure(final String id) { private void onThreadRequestFailure(final String id, final Throwable throwable) {
View view = getView(); View view = getView();
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
if (view != null) { if (view != null) {
@ -516,7 +495,7 @@ public final class ViewThreadFragment extends SFragment implements
}) })
.show(); .show();
} else { } else {
Log.e(TAG, "Couldn't display thread fetch error message"); Log.e(TAG, "Network request failed", throwable);
} }
} }

View File

@ -56,14 +56,7 @@ interface MastodonApi {
@Query("max_id") maxId: String?, @Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?, @Query("since_id") sinceId: String?,
@Query("limit") limit: Int? @Query("limit") limit: Int?
): Call<List<Status>> ): Single<Response<List<Status>>>
@GET("api/v1/timelines/home")
fun homeTimelineSingle(
@Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?,
@Query("limit") limit: Int?
): Single<List<Status>>
@GET("api/v1/timelines/public") @GET("api/v1/timelines/public")
fun publicTimeline( fun publicTimeline(
@ -71,7 +64,7 @@ interface MastodonApi {
@Query("max_id") maxId: String?, @Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?, @Query("since_id") sinceId: String?,
@Query("limit") limit: Int? @Query("limit") limit: Int?
): Call<List<Status>> ): Single<Response<List<Status>>>
@GET("api/v1/timelines/tag/{hashtag}") @GET("api/v1/timelines/tag/{hashtag}")
fun hashtagTimeline( fun hashtagTimeline(
@ -81,7 +74,7 @@ interface MastodonApi {
@Query("max_id") maxId: String?, @Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?, @Query("since_id") sinceId: String?,
@Query("limit") limit: Int? @Query("limit") limit: Int?
): Call<List<Status>> ): Single<Response<List<Status>>>
@GET("api/v1/timelines/list/{listId}") @GET("api/v1/timelines/list/{listId}")
fun listTimeline( fun listTimeline(
@ -89,7 +82,7 @@ interface MastodonApi {
@Query("max_id") maxId: String?, @Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?, @Query("since_id") sinceId: String?,
@Query("limit") limit: Int? @Query("limit") limit: Int?
): Call<List<Status>> ): Single<Response<List<Status>>>
@GET("api/v1/notifications") @GET("api/v1/notifications")
fun notifications( fun notifications(
@ -97,7 +90,7 @@ interface MastodonApi {
@Query("since_id") sinceId: String?, @Query("since_id") sinceId: String?,
@Query("limit") limit: Int?, @Query("limit") limit: Int?,
@Query("exclude_types[]") excludes: Set<Notification.Type>? @Query("exclude_types[]") excludes: Set<Notification.Type>?
): Call<List<Notification>> ): Single<Response<List<Notification>>>
@GET("api/v1/markers") @GET("api/v1/markers")
fun markersWithAuth( fun markersWithAuth(
@ -114,12 +107,7 @@ interface MastodonApi {
): Single<List<Notification>> ): Single<List<Notification>>
@POST("api/v1/notifications/clear") @POST("api/v1/notifications/clear")
fun clearNotifications(): Call<ResponseBody> fun clearNotifications(): Single<ResponseBody>
@GET("api/v1/notifications/{id}")
fun notification(
@Path("id") notificationId: String
): Call<Notification>
@Multipart @Multipart
@POST("api/v1/media") @POST("api/v1/media")
@ -146,17 +134,12 @@ interface MastodonApi {
@GET("api/v1/statuses/{id}") @GET("api/v1/statuses/{id}")
fun status( fun status(
@Path("id") statusId: String @Path("id") statusId: String
): Call<Status>
@GET("api/v1/statuses/{id}")
fun statusSingle(
@Path("id") statusId: String
): Single<Status> ): Single<Status>
@GET("api/v1/statuses/{id}/context") @GET("api/v1/statuses/{id}/context")
fun statusContext( fun statusContext(
@Path("id") statusId: String @Path("id") statusId: String
): Call<StatusContext> ): Single<StatusContext>
@GET("api/v1/statuses/{id}/reblogged_by") @GET("api/v1/statuses/{id}/reblogged_by")
fun statusRebloggedBy( fun statusRebloggedBy(
@ -295,7 +278,7 @@ interface MastodonApi {
@Query("exclude_replies") excludeReplies: Boolean?, @Query("exclude_replies") excludeReplies: Boolean?,
@Query("only_media") onlyMedia: Boolean?, @Query("only_media") onlyMedia: Boolean?,
@Query("pinned") pinned: Boolean? @Query("pinned") pinned: Boolean?
): Call<List<Status>> ): Single<Response<List<Status>>>
@GET("api/v1/accounts/{id}/followers") @GET("api/v1/accounts/{id}/followers")
fun accountFollowers( fun accountFollowers(
@ -398,14 +381,14 @@ interface MastodonApi {
@Query("max_id") maxId: String?, @Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?, @Query("since_id") sinceId: String?,
@Query("limit") limit: Int? @Query("limit") limit: Int?
): Call<List<Status>> ): Single<Response<List<Status>>>
@GET("api/v1/bookmarks") @GET("api/v1/bookmarks")
fun bookmarks( fun bookmarks(
@Query("max_id") maxId: String?, @Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?, @Query("since_id") sinceId: String?,
@Query("limit") limit: Int? @Query("limit") limit: Int?
): Call<List<Status>> ): Single<Response<List<Status>>>
@GET("api/v1/follow_requests") @GET("api/v1/follow_requests")
fun followRequests( fun followRequests(
@ -415,20 +398,10 @@ interface MastodonApi {
@POST("api/v1/follow_requests/{id}/authorize") @POST("api/v1/follow_requests/{id}/authorize")
fun authorizeFollowRequest( fun authorizeFollowRequest(
@Path("id") accountId: String @Path("id") accountId: String
): Call<Relationship>
@POST("api/v1/follow_requests/{id}/reject")
fun rejectFollowRequest(
@Path("id") accountId: String
): Call<Relationship>
@POST("api/v1/follow_requests/{id}/authorize")
fun authorizeFollowRequestObservable(
@Path("id") accountId: String
): Single<Relationship> ): Single<Relationship>
@POST("api/v1/follow_requests/{id}/reject") @POST("api/v1/follow_requests/{id}/reject")
fun rejectFollowRequestObservable( fun rejectFollowRequest(
@Path("id") accountId: String @Path("id") accountId: String
): Single<Relationship> ): Single<Relationship>

View File

@ -66,9 +66,9 @@ class TimelineRepositoryImpl(
sinceIdMinusOne: String?, limit: Int, sinceIdMinusOne: String?, limit: Int,
accountId: Long, requestMode: TimelineRequestMode accountId: Long, requestMode: TimelineRequestMode
): Single<out List<TimelineStatus>> { ): Single<out List<TimelineStatus>> {
return mastodonApi.homeTimelineSingle(maxId, sinceIdMinusOne, limit + 1) return mastodonApi.homeTimeline(maxId, sinceIdMinusOne, limit + 1)
.map { statuses -> .map { response ->
this.saveStatusesToDb(accountId, statuses, maxId, sinceId) this.saveStatusesToDb(accountId, response.body().orEmpty(), maxId, sinceId)
} }
.flatMap { statuses -> .flatMap { statuses ->
this.addFromDbIfNeeded(accountId, statuses, maxId, sinceId, limit, requestMode) this.addFromDbIfNeeded(accountId, statuses, maxId, sinceId, limit, requestMode)
@ -85,7 +85,7 @@ class TimelineRepositoryImpl(
private fun addFromDbIfNeeded(accountId: Long, statuses: List<Either<Placeholder, Status>>, private fun addFromDbIfNeeded(accountId: Long, statuses: List<Either<Placeholder, Status>>,
maxId: String?, sinceId: String?, limit: Int, maxId: String?, sinceId: String?, limit: Int,
requestMode: TimelineRequestMode requestMode: TimelineRequestMode
): Single<List<TimelineStatus>>? { ): Single<List<TimelineStatus>> {
return if (requestMode != NETWORK && statuses.size < 2) { return if (requestMode != NETWORK && statuses.size < 2) {
val newMaxID = if (statuses.isEmpty()) { val newMaxID = if (statuses.isEmpty()) {
maxId maxId

View File

@ -28,6 +28,7 @@ import org.mockito.ArgumentMatchers.*
import org.mockito.Mock import org.mockito.Mock
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import retrofit2.Response
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -76,8 +77,8 @@ class TimelineRepositoryTest {
makeStatus("3"), makeStatus("3"),
makeStatus("2") makeStatus("2")
) )
whenever(mastodonApi.homeTimelineSingle(isNull(), isNull(), anyInt())) whenever(mastodonApi.homeTimeline(isNull(), isNull(), anyInt()))
.thenReturn(Single.just(statuses)) .thenReturn(Single.just(Response.success(statuses)))
val result = subject.getStatuses(null, null, null, limit, TimelineRequestMode.NETWORK) val result = subject.getStatuses(null, null, null, limit, TimelineRequestMode.NETWORK)
.blockingGet() .blockingGet()
@ -107,8 +108,8 @@ class TimelineRepositoryTest {
) )
val sinceId = "2" val sinceId = "2"
val sinceIdMinusOne = "1" val sinceIdMinusOne = "1"
whenever(mastodonApi.homeTimelineSingle(null, sinceIdMinusOne, limit + 1)) whenever(mastodonApi.homeTimeline(null, sinceIdMinusOne, limit + 1))
.thenReturn(Single.just(response)) .thenReturn(Single.just(Response.success(response)))
val result = subject.getStatuses(null, sinceId, sinceIdMinusOne, limit, val result = subject.getStatuses(null, sinceId, sinceIdMinusOne, limit,
TimelineRequestMode.NETWORK) TimelineRequestMode.NETWORK)
.blockingGet() .blockingGet()
@ -141,8 +142,8 @@ class TimelineRepositoryTest {
) )
val sinceId = "2" val sinceId = "2"
val sinceIdMinusOne = "1" val sinceIdMinusOne = "1"
whenever(mastodonApi.homeTimelineSingle(null, sinceIdMinusOne, limit + 1)) whenever(mastodonApi.homeTimeline(null, sinceIdMinusOne, limit + 1))
.thenReturn(Single.just(response)) .thenReturn(Single.just(Response.success(response)))
val result = subject.getStatuses(null, sinceId, sinceIdMinusOne, limit, val result = subject.getStatuses(null, sinceId, sinceIdMinusOne, limit,
TimelineRequestMode.NETWORK) TimelineRequestMode.NETWORK)
.blockingGet() .blockingGet()
@ -181,8 +182,8 @@ class TimelineRepositoryTest {
val sinceId = "2" val sinceId = "2"
val sinceIdMinusOne = "1" val sinceIdMinusOne = "1"
val maxId = "3" val maxId = "3"
whenever(mastodonApi.homeTimelineSingle(maxId, sinceIdMinusOne, limit + 1)) whenever(mastodonApi.homeTimeline(maxId, sinceIdMinusOne, limit + 1))
.thenReturn(Single.just(response)) .thenReturn(Single.just(Response.success(response)))
val result = subject.getStatuses(maxId, sinceId, sinceIdMinusOne, limit, val result = subject.getStatuses(maxId, sinceId, sinceIdMinusOne, limit,
TimelineRequestMode.NETWORK) TimelineRequestMode.NETWORK)
.blockingGet() .blockingGet()
@ -224,8 +225,8 @@ class TimelineRepositoryTest {
val sinceId = "2" val sinceId = "2"
val sinceIdMinusOne = "1" val sinceIdMinusOne = "1"
val maxId = "4" val maxId = "4"
whenever(mastodonApi.homeTimelineSingle(maxId, sinceIdMinusOne, limit + 1)) whenever(mastodonApi.homeTimeline(maxId, sinceIdMinusOne, limit + 1))
.thenReturn(Single.just(response)) .thenReturn(Single.just(Response.success(response)))
val result = subject.getStatuses(maxId, sinceId, sinceIdMinusOne, limit, val result = subject.getStatuses(maxId, sinceId, sinceIdMinusOne, limit,
TimelineRequestMode.NETWORK) TimelineRequestMode.NETWORK)
.blockingGet() .blockingGet()
@ -263,8 +264,8 @@ class TimelineRepositoryTest {
dbResult.status = dbStatus.toEntity(account.id, gson) dbResult.status = dbStatus.toEntity(account.id, gson)
dbResult.account = status.account.toEntity(account.id, gson) dbResult.account = status.account.toEntity(account.id, gson)
whenever(mastodonApi.homeTimelineSingle(any(), any(), any())) whenever(mastodonApi.homeTimeline(any(), any(), any()))
.thenReturn(Single.just(listOf(status))) .thenReturn(Single.just(Response.success((listOf(status)))))
whenever(timelineDao.getStatusesForAccount(account.id, status.id, null, 30)) whenever(timelineDao.getStatusesForAccount(account.id, status.id, null, 30))
.thenReturn(Single.just(listOf(dbResult))) .thenReturn(Single.just(listOf(dbResult)))
val result = subject.getStatuses(null, null, null, limit, TimelineRequestMode.ANY) val result = subject.getStatuses(null, null, null, limit, TimelineRequestMode.ANY)
@ -281,8 +282,8 @@ class TimelineRepositoryTest {
val dbResult2 = TimelineStatusWithAccount() val dbResult2 = TimelineStatusWithAccount()
dbResult2.status = Placeholder("1").toEntity(account.id) dbResult2.status = Placeholder("1").toEntity(account.id)
whenever(mastodonApi.homeTimelineSingle(any(), any(), any())) whenever(mastodonApi.homeTimeline(any(), any(), any()))
.thenReturn(Single.just(listOf(status))) .thenReturn(Single.just(Response.success(listOf(status))))
whenever(timelineDao.getStatusesForAccount(account.id, status.id, null, 30)) whenever(timelineDao.getStatusesForAccount(account.id, status.id, null, 30))
.thenReturn(Single.just(listOf(dbResult, dbResult2))) .thenReturn(Single.just(listOf(dbResult, dbResult2)))
val result = subject.getStatuses(null, null, null, limit, TimelineRequestMode.ANY) val result = subject.getStatuses(null, null, null, limit, TimelineRequestMode.ANY)