1
0
Fork 0
mirror of https://github.com/TeamNewPipe/NewPipe.git synced 2025-10-03 17:59:41 +02:00

Merge pull request #12604 from Isira-Seneviratne/Refactor-EmptyState

This commit is contained in:
Stypox 2025-09-06 15:33:49 +02:00 committed by GitHub
commit b2d89a41fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 77 additions and 132 deletions

View file

@ -3,6 +3,7 @@ package org.schabi.newpipe.fragments.list.channel;
import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor;
import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor;
import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
@ -45,7 +46,6 @@ import org.schabi.newpipe.ktx.AnimationType;
import org.schabi.newpipe.local.feed.notifications.NotificationHelper; import org.schabi.newpipe.local.feed.notifications.NotificationHelper;
import org.schabi.newpipe.local.subscription.SubscriptionManager; import org.schabi.newpipe.local.subscription.SubscriptionManager;
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec; import org.schabi.newpipe.ui.emptystate.EmptyStateSpec;
import org.schabi.newpipe.ui.emptystate.EmptyStateUtil;
import org.schabi.newpipe.util.ChannelTabHelper; import org.schabi.newpipe.util.ChannelTabHelper;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
@ -200,10 +200,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
protected void initViews(final View rootView, final Bundle savedInstanceState) { protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
EmptyStateUtil.setEmptyStateComposable( setEmptyStateComposable(binding.emptyStateView, EmptyStateSpec.ContentNotSupported);
binding.emptyStateView,
EmptyStateSpec.Companion.getContentNotSupported()
);
tabAdapter = new TabAdapter(getChildFragmentManager()); tabAdapter = new TabAdapter(getChildFragmentManager());
binding.viewPager.setAdapter(tabAdapter); binding.viewPager.setAdapter(tabAdapter);

View file

@ -3,6 +3,7 @@ package org.schabi.newpipe.fragments.list.search;
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags; import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
import static org.schabi.newpipe.extractor.utils.Utils.isBlank; import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
@ -66,7 +67,6 @@ import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec; import org.schabi.newpipe.ui.emptystate.EmptyStateSpec;
import org.schabi.newpipe.ui.emptystate.EmptyStateUtil;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
@ -357,9 +357,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
protected void initViews(final View rootView, final Bundle savedInstanceState) { protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
EmptyStateUtil.setEmptyStateComposable( setEmptyStateComposable(searchBinding.emptyStateView, EmptyStateSpec.NoSearchResult);
searchBinding.emptyStateView,
EmptyStateSpec.Companion.getNoSearchResult());
searchBinding.suggestionsList.setAdapter(suggestionListAdapter); searchBinding.suggestionsList.setAdapter(suggestionListAdapter);
// animations are just strange and useless, since the suggestions keep changing too much // animations are just strange and useless, since the suggestions keep changing too much

View file

@ -15,6 +15,7 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.compose.ui.platform.ComposeView;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -125,10 +126,8 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
itemListAdapter.setUseItemHandle(true); itemListAdapter.setUseItemHandle(true);
EmptyStateUtil.setEmptyStateComposable( final ComposeView emptyView = rootView.findViewById(R.id.empty_state_view);
rootView.findViewById(R.id.empty_state_view), EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateSpec.NoBookmarkedPlaylist);
EmptyStateSpec.Companion.getNoBookmarkedPlaylist()
);
} }
@Override @Override

View file

@ -95,8 +95,7 @@ public class SelectChannelFragment extends DialogFragment {
progressBar = v.findViewById(R.id.progressBar); progressBar = v.findViewById(R.id.progressBar);
emptyView = v.findViewById(R.id.empty_state_view); emptyView = v.findViewById(R.id.empty_state_view);
EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateSpec.NoSubscriptions);
EmptyStateSpec.Companion.getNoSubscriptions());
progressBar.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE); recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.GONE); emptyView.setVisibility(View.GONE);

View file

@ -65,8 +65,7 @@ public class SelectPlaylistFragment extends DialogFragment {
recyclerView = v.findViewById(R.id.items_list); recyclerView = v.findViewById(R.id.items_list);
emptyView = v.findViewById(R.id.empty_state_view); emptyView = v.findViewById(R.id.empty_state_view);
EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateSpec.NoBookmarkedPlaylist);
EmptyStateSpec.Companion.getNoBookmarkedPlaylist());
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
final SelectPlaylistAdapter playlistAdapter = new SelectPlaylistAdapter(); final SelectPlaylistAdapter playlistAdapter = new SelectPlaylistAdapter();
recyclerView.setAdapter(playlistAdapter); recyclerView.setAdapter(playlistAdapter);

View file

@ -1,5 +1,7 @@
package org.schabi.newpipe.settings.preferencesearch; package org.schabi.newpipe.settings.preferencesearch;
import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -12,7 +14,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import org.schabi.newpipe.databinding.SettingsPreferencesearchFragmentBinding; import org.schabi.newpipe.databinding.SettingsPreferencesearchFragmentBinding;
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec; import org.schabi.newpipe.ui.emptystate.EmptyStateSpec;
import org.schabi.newpipe.ui.emptystate.EmptyStateUtil;
import java.util.List; import java.util.List;
@ -41,9 +42,7 @@ public class PreferenceSearchFragment extends Fragment {
binding = SettingsPreferencesearchFragmentBinding.inflate(inflater, container, false); binding = SettingsPreferencesearchFragmentBinding.inflate(inflater, container, false);
binding.searchResults.setLayoutManager(new LinearLayoutManager(getContext())); binding.searchResults.setLayoutManager(new LinearLayoutManager(getContext()));
EmptyStateUtil.setEmptyStateComposable( setEmptyStateComposable(binding.emptyStateView, EmptyStateSpec.NoSearchResult);
binding.emptyStateView,
EmptyStateSpec.Companion.getNoSearchMaxSizeResult());
adapter = new PreferenceSearchAdapter(); adapter = new PreferenceSearchAdapter();
adapter.setOnItemClickListener(this::onItemClicked); adapter.setOnItemClickListener(this::onItemClicked);

View file

@ -19,7 +19,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -122,28 +121,23 @@ private fun CommentRepliesDialog(
if (comments.itemCount == 0) { if (comments.itemCount == 0) {
item { item {
val refresh = comments.loadState.refresh when (val refresh = comments.loadState.refresh) {
if (refresh is LoadState.Loading) { is LoadState.Loading -> {
LoadingIndicator(modifier = Modifier.padding(top = 8.dp)) LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
} else if (refresh is LoadState.Error) { }
// TODO use error panel instead else -> {
EmptyStateComposable( // TODO use error panel instead
spec = EmptyStateSpec.DisabledComments.copy( EmptyStateComposable(
descriptionText = { spec = if (refresh is LoadState.Error) {
stringResource(R.string.error_unable_to_load_comments) EmptyStateSpec.ErrorLoadingComments
} else {
EmptyStateSpec.NoComments
}, },
), modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .heightIn(min = 128.dp)
.heightIn(min = 128.dp) )
) }
} else {
EmptyStateComposable(
spec = EmptyStateSpec.NoComments,
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 128.dp)
)
} }
} }
} else { } else {

View file

@ -15,7 +15,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@ -75,7 +74,6 @@ private fun CommentSection(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 128.dp) .heightIn(min = 128.dp)
) )
} }
} else if (count == 0) { } else if (count == 0) {
@ -111,13 +109,7 @@ private fun CommentSection(
is LoadState.Error -> { is LoadState.Error -> {
item { item {
// TODO use error panel instead // TODO use error panel instead
EmptyStateComposable( EmptyStateComposable(EmptyStateSpec.ErrorLoadingComments)
EmptyStateSpec.DisabledComments.copy(
descriptionText = {
stringResource(R.string.error_unable_to_load_comments)
}
)
)
} }
} }
@ -134,11 +126,7 @@ private fun CommentSection(
item { item {
// TODO use error panel instead // TODO use error panel instead
EmptyStateComposable( EmptyStateComposable(
spec = EmptyStateSpec.DisabledComments.copy( spec = EmptyStateSpec.ErrorLoadingComments,
descriptionText = {
stringResource(R.string.error_unable_to_load_comments)
}
),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 128.dp) .heightIn(min = 128.dp)

View file

@ -1,6 +1,7 @@
package org.schabi.newpipe.ui.emptystate package org.schabi.newpipe.ui.emptystate
import android.graphics.Color import android.graphics.Color
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -22,25 +23,14 @@ import org.schabi.newpipe.ui.theme.AppTheme
fun EmptyStateComposable( fun EmptyStateComposable(
spec: EmptyStateSpec, spec: EmptyStateSpec,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) = EmptyStateComposable(
emojiText = spec.emojiText(),
descriptionText = spec.descriptionText(),
modifier = modifier
)
@Composable
private fun EmptyStateComposable(
emojiText: String,
descriptionText: String,
modifier: Modifier = Modifier,
) { ) {
Column( Column(
modifier = modifier, modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center,
) { ) {
Text( Text(
text = emojiText, text = spec.emojiText,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
@ -49,7 +39,7 @@ private fun EmptyStateComposable(
modifier = Modifier modifier = Modifier
.padding(top = 6.dp) .padding(top = 6.dp)
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp),
text = descriptionText, text = stringResource(spec.descriptionText),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
@ -82,66 +72,48 @@ fun EmptyStateComposableNoCommentPreview() {
} }
} }
data class EmptyStateSpec( enum class EmptyStateSpec(
val emojiText: @Composable () -> String, val emojiText: String,
val descriptionText: @Composable () -> String, @field:StringRes val descriptionText: Int,
) { ) {
companion object { GenericError(
emojiText = "¯\\_(ツ)_/¯",
val GenericError = descriptionText = R.string.empty_list_subtitle,
EmptyStateSpec( ),
emojiText = { "¯\\_(ツ)_/¯" }, NoVideos(
descriptionText = { stringResource(id = R.string.empty_list_subtitle) }, emojiText = "(╯°-°)╯",
) descriptionText = R.string.no_videos,
),
val NoVideos = NoComments(
EmptyStateSpec( emojiText = "¯\\_(╹x╹)_/¯",
emojiText = { "(╯°-°)╯" }, descriptionText = R.string.no_comments,
descriptionText = { stringResource(id = R.string.no_videos) }, ),
) DisabledComments(
emojiText = "¯\\_(╹x╹)_/¯",
val NoComments = descriptionText = R.string.comments_are_disabled,
EmptyStateSpec( ),
ErrorLoadingComments(
emojiText = { "¯\\_(╹x╹)_/¯" }, emojiText = "¯\\_(╹x╹)_/¯",
descriptionText = { stringResource(id = R.string.no_comments) }, descriptionText = R.string.error_unable_to_load_comments,
) ),
NoSearchResult(
val DisabledComments = emojiText = "╰(°●°╰)",
NoComments.copy( descriptionText = R.string.search_no_results,
descriptionText = { stringResource(id = R.string.comments_are_disabled) }, ),
) ContentNotSupported(
emojiText = "(︶︹︺)",
val NoSearchResult = descriptionText = R.string.content_not_supported,
NoComments.copy( ),
emojiText = { "╰(°●°╰)" }, NoBookmarkedPlaylist(
descriptionText = { stringResource(id = R.string.search_no_results) } emojiText = "(╥﹏╥)",
) descriptionText = R.string.no_playlist_bookmarked_yet,
),
val NoSearchMaxSizeResult = NoSubscriptionsHint(
NoSearchResult emojiText = "(꩜ᯅ꩜)",
descriptionText = R.string.import_subscriptions_hint,
val ContentNotSupported = ),
NoComments.copy( NoSubscriptions(
emojiText = { "(︶︹︺)" }, emojiText = "(꩜ᯅ꩜)",
descriptionText = { stringResource(id = R.string.content_not_supported) }, descriptionText = R.string.no_channel_subscribed_yet,
) ),
val NoBookmarkedPlaylist =
EmptyStateSpec(
emojiText = { "(╥﹏╥)" },
descriptionText = { stringResource(id = R.string.no_playlist_bookmarked_yet) },
)
val NoSubscriptionsHint =
EmptyStateSpec(
emojiText = { "(꩜ᯅ꩜)" },
descriptionText = { stringResource(id = R.string.import_subscriptions_hint) },
)
val NoSubscriptions =
NoSubscriptionsHint.copy(
descriptionText = { stringResource(id = R.string.no_channel_subscribed_yet) },
)
}
} }