Error artwork (#1000)
* Add new Elephant Friend images. Use them in ListsActivity. * Add error images to AccountListFragment * Add error images to Timeline & Notifications fragment. Needs rework. * Introduce BackgroundMessageView. Use it in AccountList. * Use correct button style for BackgroundMessageView Co-Authored-By: charlag <charlag@tutanota.com> * Use BackgroundMessageView * Add BackgroundMessageView docs * Re-color and document elephants * Apply feedback, disable refresh when error is shown * Fix string typo
This commit is contained in:
parent
6e610b1d9d
commit
c0c73f5c06
13 changed files with 652 additions and 188 deletions
|
@ -3,25 +3,31 @@ package com.keylesspalace.tusky
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.LoadingState.*
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.MastoList
|
||||
import com.keylesspalace.tusky.fragment.TimelineFragment
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial
|
||||
import com.mikepenz.iconics.IconicsDrawable
|
||||
import kotlinx.android.synthetic.main.activity_lists.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import java.io.IOException
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -35,13 +41,17 @@ interface ListsView {
|
|||
}
|
||||
|
||||
|
||||
data class State(val lists: List<MastoList>, val isLoading: Boolean)
|
||||
enum class LoadingState {
|
||||
INITIAL, LOADING, LOADED, ERROR_NETWORK, ERROR_OTHER
|
||||
}
|
||||
|
||||
data class State(val lists: List<MastoList>, val loadingState: LoadingState)
|
||||
|
||||
class ListsViewModel(private val api: MastodonApi) {
|
||||
|
||||
private var _view: WeakReference<ListsView>? = null
|
||||
private val view: ListsView? get() = _view?.get()
|
||||
private var state = State(listOf(), false)
|
||||
private var state = State(listOf(), INITIAL)
|
||||
|
||||
fun attach(view: ListsView) {
|
||||
this._view = WeakReference(view)
|
||||
|
@ -57,17 +67,23 @@ class ListsViewModel(private val api: MastodonApi) {
|
|||
view?.openTimeline(id)
|
||||
}
|
||||
|
||||
fun retryLoading() {
|
||||
loadIfNeeded()
|
||||
}
|
||||
|
||||
private fun loadIfNeeded() {
|
||||
if (state.isLoading || !state.lists.isEmpty()) return
|
||||
updateState(state.copy(isLoading = false))
|
||||
if (state.loadingState == LOADING || !state.lists.isEmpty()) return
|
||||
updateState(state.copy(loadingState = LOADING))
|
||||
|
||||
api.getLists().enqueue(object : retrofit2.Callback<List<MastoList>> {
|
||||
override fun onResponse(call: Call<List<MastoList>>, response: Response<List<MastoList>>) {
|
||||
updateState(state.copy(lists = response.body() ?: listOf(), isLoading = false))
|
||||
updateState(state.copy(lists = response.body() ?: listOf(), loadingState = LOADED))
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<MastoList>>, t: Throwable?) {
|
||||
updateState(state.copy(isLoading = false))
|
||||
override fun onFailure(call: Call<List<MastoList>>, err: Throwable?) {
|
||||
updateState(state.copy(
|
||||
loadingState = if (err is IOException) ERROR_NETWORK else ERROR_OTHER
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -94,9 +110,6 @@ class ListsActivity : BaseActivity(), ListsView, Injectable {
|
|||
@Inject
|
||||
lateinit var mastodonApi: MastodonApi
|
||||
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private lateinit var progressBar: ProgressBar
|
||||
|
||||
private lateinit var viewModel: ListsViewModel
|
||||
private val adapter = ListsAdapter()
|
||||
|
||||
|
@ -105,8 +118,6 @@ class ListsActivity : BaseActivity(), ListsView, Injectable {
|
|||
setContentView(R.layout.activity_lists)
|
||||
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
recyclerView = findViewById(R.id.lists_recycler)
|
||||
progressBar = findViewById(R.id.progress_bar)
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
val bar = supportActionBar
|
||||
|
@ -116,9 +127,9 @@ class ListsActivity : BaseActivity(), ListsView, Injectable {
|
|||
bar.setDisplayShowHomeEnabled(true)
|
||||
}
|
||||
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView.addItemDecoration(
|
||||
listsRecycler.adapter = adapter
|
||||
listsRecycler.layoutManager = LinearLayoutManager(this)
|
||||
listsRecycler.addItemDecoration(
|
||||
DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
|
||||
|
||||
viewModel = lastNonConfigurationInstance as? ListsViewModel ?: ListsViewModel(mastodonApi)
|
||||
|
@ -137,8 +148,30 @@ class ListsActivity : BaseActivity(), ListsView, Injectable {
|
|||
|
||||
override fun update(state: State) {
|
||||
adapter.update(state.lists)
|
||||
progressBar.visibility = if (state.isLoading) View.VISIBLE else View.GONE
|
||||
|
||||
progressBar.visibility = if (state.loadingState == LOADING) View.VISIBLE else View.GONE
|
||||
when (state.loadingState) {
|
||||
INITIAL, LOADING -> messageView.hide()
|
||||
ERROR_NETWORK -> {
|
||||
messageView.show()
|
||||
messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
viewModel.retryLoading()
|
||||
}
|
||||
}
|
||||
ERROR_OTHER -> {
|
||||
messageView.show()
|
||||
messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
viewModel.retryLoading()
|
||||
}
|
||||
}
|
||||
LOADED ->
|
||||
if (state.lists.isEmpty()) {
|
||||
messageView.show()
|
||||
messageView.setup(R.drawable.elephant_friend_empty, R.string.message_empty,
|
||||
null)
|
||||
} else {
|
||||
messageView.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun openTimeline(listId: String) {
|
||||
|
|
|
@ -16,24 +16,19 @@
|
|||
package com.keylesspalace.tusky.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.AccountActivity
|
||||
import com.keylesspalace.tusky.AccountListActivity.Type
|
||||
import com.keylesspalace.tusky.BaseActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.adapter.AccountAdapter
|
||||
import com.keylesspalace.tusky.adapter.BlocksAdapter
|
||||
import com.keylesspalace.tusky.adapter.FollowAdapter
|
||||
import com.keylesspalace.tusky.adapter.FollowRequestsAdapter
|
||||
import com.keylesspalace.tusky.adapter.MutesAdapter
|
||||
import com.keylesspalace.tusky.adapter.*
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
import com.keylesspalace.tusky.entity.Relationship
|
||||
|
@ -41,14 +36,15 @@ import com.keylesspalace.tusky.interfaces.AccountActionListener
|
|||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.view.EndlessOnScrollListener
|
||||
import kotlinx.android.synthetic.main.fragment_account_list.*
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
||||
|
||||
|
@ -83,7 +79,7 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
|||
divider.setDrawable(drawable)
|
||||
recyclerView.addItemDecoration(divider)
|
||||
|
||||
adapter = when(type) {
|
||||
adapter = when (type) {
|
||||
Type.BLOCKS -> BlocksAdapter(this)
|
||||
Type.MUTES -> MutesAdapter(this)
|
||||
Type.FOLLOW_REQUESTS -> FollowRequestsAdapter(this)
|
||||
|
@ -143,7 +139,7 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
|||
val mutesAdapter = adapter as MutesAdapter
|
||||
val unmutedUser = mutesAdapter.removeItem(position)
|
||||
|
||||
if(unmutedUser != null) {
|
||||
if (unmutedUser != null) {
|
||||
Snackbar.make(recyclerView, R.string.confirmation_unmuted, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_undo) {
|
||||
mutesAdapter.addItem(unmutedUser, position)
|
||||
|
@ -193,7 +189,7 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
|||
val blocksAdapter = adapter as BlocksAdapter
|
||||
val unblockedUser = blocksAdapter.removeItem(position)
|
||||
|
||||
if(unblockedUser != null) {
|
||||
if (unblockedUser != null) {
|
||||
Snackbar.make(recyclerView, R.string.confirmation_unblocked, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.action_undo) {
|
||||
blocksAdapter.addItem(unblockedUser, position)
|
||||
|
@ -311,11 +307,36 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
|||
|
||||
fetching = false
|
||||
|
||||
if (adapter.itemCount == 0) {
|
||||
messageView.show()
|
||||
messageView.setup(
|
||||
R.drawable.elephant_friend_empty,
|
||||
R.string.message_empty,
|
||||
null
|
||||
)
|
||||
} else {
|
||||
messageView.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFetchAccountsFailure(exception: Exception) {
|
||||
fetching = false
|
||||
Log.e(TAG, "Fetch failure", exception)
|
||||
|
||||
if (adapter.itemCount == 0) {
|
||||
messageView.show()
|
||||
if (exception is IOException) {
|
||||
messageView.setup(R.drawable.elephant_offline, R.string.error_network) {
|
||||
messageView.hide()
|
||||
this.fetchAccounts(null)
|
||||
}
|
||||
} else {
|
||||
messageView.setup(R.drawable.elephant_error, R.string.error_generic) {
|
||||
messageView.hide()
|
||||
this.fetchAccounts(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -16,31 +16,19 @@
|
|||
package com.keylesspalace.tusky.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.keylesspalace.tusky.MainActivity;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.adapter.NotificationsAdapter;
|
||||
|
@ -64,10 +52,12 @@ import com.keylesspalace.tusky.util.ListUtils;
|
|||
import com.keylesspalace.tusky.util.PairedList;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
import com.keylesspalace.tusky.util.ViewDataUtils;
|
||||
import com.keylesspalace.tusky.view.BackgroundMessageView;
|
||||
import com.keylesspalace.tusky.view.EndlessOnScrollListener;
|
||||
import com.keylesspalace.tusky.viewdata.NotificationViewData;
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
@ -76,7 +66,18 @@ import java.util.Objects;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import kotlin.Unit;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
|
@ -125,7 +126,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
private SwipeRefreshLayout swipeRefreshLayout;
|
||||
private RecyclerView recyclerView;
|
||||
private ProgressBar progressBar;
|
||||
private TextView nothingMessageView;
|
||||
private BackgroundMessageView statusView;
|
||||
|
||||
private LinearLayoutManager layoutManager;
|
||||
private EndlessOnScrollListener scrollListener;
|
||||
|
@ -177,7 +178,7 @@ public class NotificationsFragment extends SFragment implements
|
|||
swipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh_layout);
|
||||
recyclerView = rootView.findViewById(R.id.recycler_view);
|
||||
progressBar = rootView.findViewById(R.id.progress_bar);
|
||||
nothingMessageView = rootView.findViewById(R.id.nothing_message);
|
||||
statusView = rootView.findViewById(R.id.statusView);
|
||||
|
||||
swipeRefreshLayout.setOnRefreshListener(this);
|
||||
swipeRefreshLayout.setColorSchemeResources(R.color.tusky_blue);
|
||||
|
@ -208,20 +209,12 @@ public class NotificationsFragment extends SFragment implements
|
|||
bottomId = null;
|
||||
|
||||
((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
|
||||
setupNothingView();
|
||||
|
||||
sendFetchNotificationsRequest(null, null, FetchEnd.BOTTOM, -1);
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void setupNothingView() {
|
||||
Drawable top = AppCompatResources.getDrawable(Objects.requireNonNull(getContext()),
|
||||
R.drawable.elephant_friend_empty);
|
||||
nothingMessageView.setCompoundDrawablesWithIntrinsicBounds(null, top, null, null);
|
||||
nothingMessageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void handleFavEvent(FavoriteEvent event) {
|
||||
Pair<Integer, Notification> posAndNotification =
|
||||
findReplyPosition(event.getStatusId());
|
||||
|
@ -332,6 +325,8 @@ public class NotificationsFragment extends SFragment implements
|
|||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
swipeRefreshLayout.setEnabled(true);
|
||||
this.statusView.setVisibility(View.GONE);
|
||||
Either<Placeholder, Notification> first = CollectionsKt.firstOrNull(this.notifications);
|
||||
String topId;
|
||||
if (first != null && first.isRight()) {
|
||||
|
@ -721,9 +716,9 @@ public class NotificationsFragment extends SFragment implements
|
|||
}
|
||||
|
||||
if (notifications.size() == 0 && adapter.getItemCount() == 0) {
|
||||
nothingMessageView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
nothingMessageView.setVisibility(View.GONE);
|
||||
this.statusView.setVisibility(View.VISIBLE);
|
||||
this.statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null);
|
||||
|
||||
}
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
@ -736,6 +731,22 @@ public class NotificationsFragment extends SFragment implements
|
|||
new NotificationViewData.Placeholder(false);
|
||||
notifications.setPairedItem(position, placeholderVD);
|
||||
adapter.updateItemWithNotify(position, placeholderVD, true);
|
||||
} else if (this.notifications.isEmpty()) {
|
||||
this.statusView.setVisibility(View.VISIBLE);
|
||||
swipeRefreshLayout.setEnabled(false);
|
||||
if (exception instanceof IOException) {
|
||||
this.statusView.setup(R.drawable.elephant_offline, R.string.error_network, __ -> {
|
||||
this.progressBar.setVisibility(View.VISIBLE);
|
||||
this.onRefresh();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
} else {
|
||||
this.statusView.setup(R.drawable.elephant_error, R.string.error_generic, __ -> {
|
||||
this.progressBar.setVisibility(View.VISIBLE);
|
||||
this.onRefresh();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
}
|
||||
Log.e(TAG, "Fetch failure: " + exception.getMessage());
|
||||
progressBar.setVisibility(View.GONE);
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
@ -56,9 +55,11 @@ import com.keylesspalace.tusky.util.ListUtils;
|
|||
import com.keylesspalace.tusky.util.PairedList;
|
||||
import com.keylesspalace.tusky.util.ThemeUtils;
|
||||
import com.keylesspalace.tusky.util.ViewDataUtils;
|
||||
import com.keylesspalace.tusky.view.BackgroundMessageView;
|
||||
import com.keylesspalace.tusky.view.EndlessOnScrollListener;
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -71,7 +72,6 @@ import javax.inject.Inject;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.core.util.Pair;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
|
@ -86,6 +86,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator;
|
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import at.connyduck.sparkbutton.helpers.Utils;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import kotlin.Unit;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
|
@ -137,7 +138,7 @@ public class TimelineFragment extends SFragment implements
|
|||
private SwipeRefreshLayout swipeRefreshLayout;
|
||||
private RecyclerView recyclerView;
|
||||
private ProgressBar progressBar;
|
||||
private TextView nothingMessageView;
|
||||
private BackgroundMessageView statusView;
|
||||
|
||||
private TimelineAdapter adapter;
|
||||
private Kind kind;
|
||||
|
@ -220,13 +221,12 @@ public class TimelineFragment extends SFragment implements
|
|||
recyclerView = rootView.findViewById(R.id.recycler_view);
|
||||
swipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh_layout);
|
||||
progressBar = rootView.findViewById(R.id.progress_bar);
|
||||
nothingMessageView = rootView.findViewById(R.id.nothing_message);
|
||||
statusView = rootView.findViewById(R.id.statusView);
|
||||
|
||||
setupSwipeRefreshLayout();
|
||||
setupRecyclerView();
|
||||
updateAdapter();
|
||||
setupTimelinePreferences();
|
||||
setupNothingView();
|
||||
|
||||
if (statuses.isEmpty()) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
|
@ -376,10 +376,15 @@ public class TimelineFragment extends SFragment implements
|
|||
}
|
||||
}
|
||||
if (statuses.size() == 0) {
|
||||
nothingMessageView.setVisibility(View.VISIBLE);
|
||||
showNothing();
|
||||
}
|
||||
}
|
||||
|
||||
private void showNothing() {
|
||||
statusView.setVisibility(View.VISIBLE);
|
||||
statusView.setup(R.drawable.elephant_friend_empty, R.string.message_empty, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
@ -502,14 +507,10 @@ public class TimelineFragment extends SFragment implements
|
|||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private void setupNothingView() {
|
||||
Drawable top = AppCompatResources.getDrawable(requireContext(), R.drawable.elephant_friend_empty);
|
||||
nothingMessageView.setCompoundDrawablesWithIntrinsicBounds(null, top, null, null);
|
||||
nothingMessageView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
swipeRefreshLayout.setEnabled(true);
|
||||
this.statusView.setVisibility(View.GONE);
|
||||
if (this.initialUpdateFailed) {
|
||||
updateCurrent();
|
||||
} else {
|
||||
|
@ -863,7 +864,6 @@ public class TimelineFragment extends SFragment implements
|
|||
|
||||
private void sendFetchTimelineRequest(@Nullable String fromId, @Nullable String uptoId,
|
||||
final FetchEnd fetchEnd, final int pos) {
|
||||
|
||||
if (kind == Kind.HOME) {
|
||||
TimelineRequestMode mode;
|
||||
// allow getting old statuses/fallbacks for network only for for bottom loading
|
||||
|
@ -872,13 +872,13 @@ public class TimelineFragment extends SFragment implements
|
|||
} else {
|
||||
mode = TimelineRequestMode.NETWORK;
|
||||
}
|
||||
timelineRepo.getStatuses(fromId, uptoId, LOAD_AT_ONCE, mode)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
|
||||
.subscribe(
|
||||
(result) -> onFetchTimelineSuccess(result, fetchEnd, pos),
|
||||
(err) -> onFetchTimelineFailure(new Exception(err), fetchEnd, pos)
|
||||
);
|
||||
timelineRepo.getStatuses(fromId, uptoId, LOAD_AT_ONCE, mode)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
|
||||
.subscribe(
|
||||
(result) -> onFetchTimelineSuccess(result, fetchEnd, pos),
|
||||
(err) -> onFetchTimelineFailure(new Exception(err), fetchEnd, pos)
|
||||
);
|
||||
} else {
|
||||
Callback<List<Status>> callback = new Callback<List<Status>>() {
|
||||
@Override
|
||||
|
@ -946,10 +946,11 @@ public class TimelineFragment extends SFragment implements
|
|||
updateBottomLoadingState(fetchEnd);
|
||||
progressBar.setVisibility(View.GONE);
|
||||
swipeRefreshLayout.setRefreshing(false);
|
||||
swipeRefreshLayout.setEnabled(true);
|
||||
if (this.statuses.size() == 0) {
|
||||
nothingMessageView.setVisibility(View.VISIBLE);
|
||||
this.showNothing();
|
||||
} else {
|
||||
nothingMessageView.setVisibility(View.GONE);
|
||||
this.statusView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -968,6 +969,22 @@ public class TimelineFragment extends SFragment implements
|
|||
newViewData = new StatusViewData.Placeholder(placeholder.getId(), false);
|
||||
statuses.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
} else if (this.statuses.isEmpty()) {
|
||||
swipeRefreshLayout.setEnabled(false);
|
||||
this.statusView.setVisibility(View.VISIBLE);
|
||||
if (exception instanceof IOException) {
|
||||
this.statusView.setup(R.drawable.elephant_offline, R.string.error_network, __ -> {
|
||||
this.progressBar.setVisibility(View.VISIBLE);
|
||||
this.onRefresh();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
} else {
|
||||
this.statusView.setup(R.drawable.elephant_error, R.string.error_generic, __ -> {
|
||||
this.progressBar.setVisibility(View.VISIBLE);
|
||||
this.onRefresh();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Log.e(TAG, "Fetch Failure: " + exception.getMessage());
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package com.keylesspalace.tusky.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.util.visible
|
||||
import kotlinx.android.synthetic.main.view_background_message.view.*
|
||||
|
||||
|
||||
/**
|
||||
* This view is used for screens with downloadable content which may fail.
|
||||
* Can show an image, text and button below them.
|
||||
*/
|
||||
class BackgroundMessageView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.view_background_message, this)
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
orientation = VERTICAL
|
||||
|
||||
if (isInEditMode) {
|
||||
setup(R.drawable.elephant_offline, R.string.error_network) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup image, message and button.
|
||||
* If [clickListener] is `null` then the button will be hidden.
|
||||
*/
|
||||
fun setup(@DrawableRes imageRes: Int, @StringRes messageRes: Int,
|
||||
clickListener: ((v: View) -> Unit)?) {
|
||||
messageTextView.setText(messageRes)
|
||||
messageTextView.setCompoundDrawablesWithIntrinsicBounds(0, imageRes, 0, 0)
|
||||
button.setOnClickListener(clickListener)
|
||||
button.visible(clickListener != null)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue