diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 652806339579..ae53f411fddc 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -1,5 +1,9 @@ *** PLEASE FOLLOW THIS FORMAT: [] [] +24.3 +----- + + 24.2 ----- * [**] Fix editor crash occurring on large posts [https://github.com/wordpress-mobile/WordPress-Android/pull/20046] diff --git a/WordPress/jetpack_metadata/release_notes.txt b/WordPress/jetpack_metadata/release_notes.txt index e7b4006244cd..2a490f10579f 100644 --- a/WordPress/jetpack_metadata/release_notes.txt +++ b/WordPress/jetpack_metadata/release_notes.txt @@ -1,6 +1,4 @@ -- Get notified when you’re working offline. -- Image block uploads pause/resume with internet connection. -- See custom gradient selections in the editor. -- Fixed forward/back arrows for right-to-left readers. -- Daily Prompt tags work properly. -- Tap Site Domain cards to manage domains. +* [**] Fix editor crash occurring on large posts [https://github.com/wordpress-mobile/WordPress-Android/pull/20046] +* [*] [Jetpack-only] Site Monitoring: Add Metrics, PHP Logs, and Web Server Logs under Site Monitoring [https://github.com/wordpress-mobile/WordPress-Android/issues/20067] +* [**] Prevent images from temporarily disappearing when uploading media [https://github.com/WordPress/gutenberg/pull/57869] +* [***] [Jetpack-only] Reader: introduced new UI/UX for content navigation and filtering [https://github.com/wordpress-mobile/WordPress-Android/pull/19978] diff --git a/WordPress/metadata/release_notes.txt b/WordPress/metadata/release_notes.txt index ab3a2cba51aa..d4aea576c6dd 100644 --- a/WordPress/metadata/release_notes.txt +++ b/WordPress/metadata/release_notes.txt @@ -1,4 +1,2 @@ -- Get notified when you’re working offline. -- Image block uploads pause when you lose internet and resume when you reconnect. -- Select a custom gradient in the editor and see a color indicator. -- “Forward” and “back” arrows are correct for right-to-left readers. +* [**] Fix editor crash occurring on large posts [https://github.com/wordpress-mobile/WordPress-Android/pull/20046] +* [**] Prevent images from temporarily disappearing when uploading media [https://github.com/WordPress/gutenberg/pull/57869] diff --git a/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartMySitePrompts.kt b/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartMySitePrompts.kt index 5861be66f0f4..4d200bdbfb34 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartMySitePrompts.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/quickstart/QuickStartMySitePrompts.kt @@ -50,7 +50,7 @@ enum class QuickStartMySitePrompts constructor( R.id.root_view_main, R.id.bottom_nav_reader_button, R.string.quick_start_dialog_follow_sites_message_short_reader, - R.drawable.ic_reader_white_24dp + R.drawable.ic_reader_selected ), UPLOAD_SITE_ICON( QuickStartStore.QUICK_START_UPLOAD_SITE_ICON_LABEL, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java index 81843376bae2..1299d325b157 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderActivityLauncher.java @@ -312,9 +312,13 @@ public static void showReaderSubs(Context context) { } public static void showReaderSubs(Context context, int selectPosition) { - Intent intent = new Intent(context, ReaderSubsActivity.class); + context.startActivity(createIntentShowReaderSubs(context, selectPosition)); + } + + public static Intent createIntentShowReaderSubs(@NonNull final Context context, final int selectPosition) { + final Intent intent = new Intent(context, ReaderSubsActivity.class); intent.putExtra(ReaderConstants.ARG_SUBS_TAB_POSITION, selectPosition); - context.startActivity(intent); + return intent; } public static void showReaderInterests(Activity activity) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderBlogFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderBlogFragment.java index f2c842c66c0a..33ea702b6a9c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderBlogFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderBlogFragment.java @@ -227,11 +227,8 @@ private ReaderBlogAdapter getBlogAdapter() { ReaderTracker.SOURCE_SETTINGS ); mAdapter.setBlogClickListener(this); - mAdapter.setDataLoadedListener(new ReaderInterfaces.DataLoadedListener() { - @Override - public void onDataLoaded(boolean isEmpty) { - checkEmptyView(); - } + mAdapter.setDataLoadedListener(isEmpty -> { + checkEmptyView(); }); } return mAdapter; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt index 49bc8607a84c..28397a28fb70 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderFragment.kt @@ -1,14 +1,10 @@ package org.wordpress.android.ui.reader import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem import android.view.View import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.core.view.MenuProvider import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider @@ -48,12 +44,11 @@ import org.wordpress.android.util.SnackbarItem.Action import org.wordpress.android.util.SnackbarItem.Info import org.wordpress.android.util.SnackbarSequencer import org.wordpress.android.viewmodel.observeEvent -import org.wordpress.android.widgets.QuickStartFocusPoint import java.util.EnumSet import javax.inject.Inject @AndroidEntryPoint -class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider, ScrollableViewInitializedListener { +class ReaderFragment : Fragment(R.layout.reader_fragment_layout), ScrollableViewInitializedListener { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @@ -70,14 +65,9 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider, lateinit var snackbarSequencer: SnackbarSequencer private lateinit var viewModel: ReaderViewModel - private var searchMenuItem: MenuItem? = null - private var settingsMenuItem: MenuItem? = null - private var settingsMenuItemFocusPoint: QuickStartFocusPoint? = null - private var binding: ReaderFragmentLayoutBinding? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - requireActivity().addMenuProvider(this, viewLifecycleOwner) binding = ReaderFragmentLayoutBinding.bind(view).apply { initTopAppBar() initViewModel(savedInstanceState) @@ -86,9 +76,6 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider, override fun onDestroyView() { super.onDestroyView() - searchMenuItem = null - settingsMenuItem = null - settingsMenuItemFocusPoint = null binding = null } @@ -102,36 +89,6 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider, activity?.let { viewModel.onScreenInBackground(it.isChangingConfigurations) } } - override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { - menuInflater.inflate(R.menu.reader_home, menu) - menu.findItem(R.id.menu_search).apply { - searchMenuItem = this - this.isVisible = viewModel.uiState.value?.searchMenuItemUiState?.isVisible ?: false - } - menu.findItem(R.id.menu_settings).apply { - settingsMenuItem = this - settingsMenuItemFocusPoint = this.actionView?.findViewById(R.id.menu_quick_start_focus_point) - this.isVisible = viewModel.uiState.value?.settingsMenuItemUiState?.isVisible ?: false - settingsMenuItemFocusPoint?.isVisible = - viewModel.uiState.value?.settingsMenuItemUiState?.showQuickStartFocusPoint ?: false - this.actionView?.setOnClickListener { viewModel.onSettingsActionClicked() } - } - } - - override fun onMenuItemSelected(menuItem: MenuItem) = when (menuItem.itemId) { - R.id.menu_search -> { - viewModel.onSearchActionClicked() - true - } - - R.id.menu_settings -> { - viewModel.onSettingsActionClicked() - true - } - - else -> false - } - private fun ReaderFragmentLayoutBinding.initTopAppBar() { readerTopBarComposeView.apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) @@ -171,10 +128,6 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider, ReaderActivityLauncher.showReaderSearch(context) } - viewModel.showSettings.observeEvent(viewLifecycleOwner) { - ReaderActivityLauncher.showReaderSubs(context) - } - viewModel.showReaderInterests.observeEvent(viewLifecycleOwner) { showReaderInterests() } @@ -225,12 +178,6 @@ class ReaderFragment : Fragment(R.layout.reader_fragment_layout), MenuProvider, initContentContainer(uiState) } } - // TODO As part of Reader IA changes this view is going to be replaced - // uiHelpers.updateVisibility(tabLayout, uiState.tabLayoutVisible) - searchMenuItem?.isVisible = uiState.searchMenuItemUiState.isVisible - settingsMenuItem?.isVisible = uiState.settingsMenuItemUiState.isVisible - settingsMenuItemFocusPoint?.isVisible = - viewModel.uiState.value?.settingsMenuItemUiState?.showQuickStartFocusPoint ?: false } private fun initContentContainer(uiState: ContentUiState) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java index 55948773f62b..ec3e175ad5bb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPostListFragment.java @@ -20,6 +20,8 @@ import android.widget.ProgressBar; import android.widget.TextView; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -214,6 +216,7 @@ public class ReaderPostListFragment extends ViewPagerFragment private int mSearchTabsPos = NO_POSITION; private boolean mIsFilterableScreen; private boolean mIsFiltered = false; + private ActivityResultLauncher mReaderSubsActivityResultLauncher; @NonNull private HashSet mCurrentUpdateActions = new HashSet<>(); /* * called by post adapter to load older posts when user scrolls to the last post @@ -652,9 +655,11 @@ private void initSubFilterViewModel(@Nullable Bundle savedInstanceState) { mSubFilterViewModel.getBottomSheetAction().observe(getViewLifecycleOwner(), event -> { event.applyIfNotHandled(action -> { if (action instanceof OpenSubsAtPage) { - ReaderActivityLauncher.showReaderSubs( - requireActivity(), - ((OpenSubsAtPage) action).getTabIndex() + mReaderSubsActivityResultLauncher.launch( + ReaderActivityLauncher.createIntentShowReaderSubs( + requireActivity(), + ((OpenSubsAtPage) action).getTabIndex() + ) ); } else if (action instanceof OpenLoginPage) { wpMainActivityViewModel.onOpenLoginPage(); @@ -846,6 +851,25 @@ public void onAttach(@NonNull Context context) { if (context instanceof BottomNavController) { mBottomNavController = (BottomNavController) context; } + + initReaderSubsActivityResultLauncher(); + } + + private void initReaderSubsActivityResultLauncher() { + mReaderSubsActivityResultLauncher = registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + result -> { + if (result.getResultCode() == Activity.RESULT_OK) { + final Intent data = result.getData(); + if (data != null) { + final boolean shouldRefreshSubscriptions = + data.getBooleanExtra(ReaderSubsActivity.RESULT_SHOULD_REFRESH_SUBSCRIPTIONS, false); + if (shouldRefreshSubscriptions) { + mSubFilterViewModel.loadSubFilters(); + } + } + } + }); } @Override diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderSubsActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderSubsActivity.java index fd0d8281fddf..9b0d02c08482 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderSubsActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderSubsActivity.java @@ -87,6 +87,8 @@ public class ReaderSubsActivity extends LocaleAwareActivity public static final int TAB_IDX_FOLLOWED_TAGS = 0; public static final int TAB_IDX_FOLLOWED_BLOGS = 1; + public static final String RESULT_SHOULD_REFRESH_SUBSCRIPTIONS = "should_refresh_subscriptions"; + @Inject AccountStore mAccountStore; @Inject ReaderTracker mReaderTracker; @@ -98,10 +100,8 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { OnBackPressedCallback callback = new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { - if (!TextUtils.isEmpty(mLastAddedTagName)) { - EventBus.getDefault().postSticky(new ReaderEvents.TagAdded(mLastAddedTagName)); - } mReaderTracker.track(Stat.READER_MANAGE_VIEW_DISMISSED); + setResult(); CompatExtensionsKt.onBackPressedCompat(getOnBackPressedDispatcher(), this); } }; @@ -171,6 +171,20 @@ public void onPageSelected(int position) { mReaderTracker.track(Stat.READER_MANAGE_VIEW_DISPLAYED); } + private void setResult() { + final Intent data = new Intent(); + boolean shouldRefreshSubscriptions = false; + if (mPageAdapter != null) { + final ReaderTagFragment readerTagFragment = mPageAdapter.getReaderTagFragment(); + final ReaderBlogFragment readerBlogFragment = mPageAdapter.getReaderBlogFragment(); + if (readerTagFragment != null && readerBlogFragment != null) { + shouldRefreshSubscriptions = readerTagFragment.hasChangedSelectedTags(); + } + } + data.putExtra(RESULT_SHOULD_REFRESH_SUBSCRIPTIONS, shouldRefreshSubscriptions); + setResult(RESULT_OK, data); + } + @Override protected void onPause() { EventBus.getDefault().unregister(this); @@ -564,12 +578,30 @@ public Object instantiateItem(ViewGroup container, int position) { } private void refreshFollowedTagFragment() { - for (Fragment fragment : mFragments) { + final ReaderTagFragment fragment = getReaderTagFragment(); + if (fragment != null) { + fragment.refresh(); + } + } + + @Nullable + private ReaderTagFragment getReaderTagFragment() { + for (final Fragment fragment : mFragments) { if (fragment instanceof ReaderTagFragment) { - ReaderTagFragment tagFragment = (ReaderTagFragment) fragment; - tagFragment.refresh(); + return (ReaderTagFragment) fragment; + } + } + return null; + } + + @Nullable + private ReaderBlogFragment getReaderBlogFragment() { + for (final Fragment fragment : mFragments) { + if (fragment instanceof ReaderBlogFragment) { + return (ReaderBlogFragment) fragment; } } + return null; } private void refreshBlogFragments(ReaderBlogType blogType) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderTagFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderTagFragment.java index 5b06e050695a..1638e7ae1eb0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderTagFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderTagFragment.java @@ -11,12 +11,17 @@ import org.wordpress.android.R; import org.wordpress.android.models.ReaderTag; +import org.wordpress.android.models.ReaderTagList; import org.wordpress.android.ui.ActionableEmptyView; import org.wordpress.android.ui.reader.adapters.ReaderTagAdapter; import org.wordpress.android.ui.reader.views.ReaderRecyclerView; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.WPActivityUtils; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + /* * fragment hosted by ReaderSubsActivity which shows followed tags */ @@ -24,6 +29,9 @@ public class ReaderTagFragment extends Fragment implements ReaderTagAdapter.TagD private ReaderRecyclerView mRecyclerView; private ReaderTagAdapter mTagAdapter; + private boolean mIsFirstDataLoaded; + private final ReaderTagList mInitialReaderTagList = new ReaderTagList(); + static ReaderTagFragment newInstance() { AppLog.d(AppLog.T.READER, "reader tag list > newInstance"); return new ReaderTagFragment(); @@ -36,6 +44,21 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, return view; } + public boolean hasChangedSelectedTags() { + final Set initialTagsSlugs = new HashSet<>(); + for (final ReaderTag readerTag : mInitialReaderTagList) { + initialTagsSlugs.add(readerTag.getTagSlug()); + } + final List currentReaderTagList = getTagAdapter().getItems(); + final Set currentTagsSlugs = new HashSet<>(); + if (currentReaderTagList != null) { + for (final ReaderTag readerTag : currentReaderTagList) { + currentTagsSlugs.add(readerTag.getTagSlug()); + } + } + return !(initialTagsSlugs.equals(currentTagsSlugs)); + } + private void checkEmptyView() { if (!isAdded() || getView() == null) { return; @@ -62,6 +85,12 @@ public void onActivityCreated(Bundle savedInstanceState) { refresh(); } + @Override + public void onAttach(Context context) { + super.onAttach(context); + mIsFirstDataLoaded = true; + } + void refresh() { if (hasTagAdapter()) { AppLog.d(AppLog.T.READER, "reader subs > refreshing tag fragment"); @@ -74,10 +103,14 @@ private ReaderTagAdapter getTagAdapter() { Context context = WPActivityUtils.getThemedContext(getActivity()); mTagAdapter = new ReaderTagAdapter(context); mTagAdapter.setTagDeletedListener(this); - mTagAdapter.setDataLoadedListener(new ReaderInterfaces.DataLoadedListener() { - @Override - public void onDataLoaded(boolean isEmpty) { - checkEmptyView(); + mTagAdapter.setDataLoadedListener(isEmpty -> { + checkEmptyView(); + if (mIsFirstDataLoaded) { + mIsFirstDataLoaded = false; + mInitialReaderTagList.clear(); + if (mTagAdapter != null && mTagAdapter.getItems() != null) { + mInitialReaderTagList.addAll(mTagAdapter.getItems()); + } } }); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderTagAdapter.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderTagAdapter.java index 57a3d3fe42a7..5dc99a307cea 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderTagAdapter.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/adapters/ReaderTagAdapter.java @@ -9,6 +9,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; import org.wordpress.android.R; @@ -93,6 +94,11 @@ public TagViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new TagViewHolder(view); } + @Nullable + public ReaderTagList getItems() { + return mTags; + } + @Override public void onBindViewHolder(TagViewHolder holder, int position) { final ReaderTag tag = mTags.get(position); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModel.kt index 67be5e9cd0ec..b2a754a06137 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModel.kt @@ -168,8 +168,6 @@ class ReaderInterestsViewModel @Inject constructor( ) ) ) - - parentViewModel?.completeQuickStartFollowSiteTaskIfNeeded() } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt index 2f97cf4fcbea..e78ad354d41e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModel.kt @@ -42,13 +42,13 @@ import org.wordpress.android.ui.reader.usecases.LoadReaderTabsUseCase import org.wordpress.android.ui.reader.utils.DateProvider import org.wordpress.android.ui.reader.utils.ReaderTopBarMenuHelper import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState -import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState.MenuItemUiState import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState.TabUiState import org.wordpress.android.ui.reader.views.compose.filter.ReaderFilterSelectedItem import org.wordpress.android.ui.reader.views.compose.filter.ReaderFilterType import org.wordpress.android.ui.utils.UiString import org.wordpress.android.ui.utils.UiString.UiStringText import org.wordpress.android.util.JetpackBrandingUtils +import org.wordpress.android.util.QuickStartUtils import org.wordpress.android.util.SnackbarSequencer import org.wordpress.android.util.distinct import org.wordpress.android.viewmodel.Event @@ -94,9 +94,6 @@ class ReaderViewModel @Inject constructor( private val _showSearch = MutableLiveData>() val showSearch: LiveData> = _showSearch - private val _showSettings = MutableLiveData>() - val showSettings: LiveData> = _showSettings - private val _showReaderInterests = MutableLiveData>() val showReaderInterests: LiveData> = _showReaderInterests @@ -138,7 +135,6 @@ class ReaderViewModel @Inject constructor( private fun loadTabs(savedInstanceState: Bundle? = null) { launch { - val currentContentUiState = _uiState.value as? ContentUiState val tagList = loadReaderTabsUseCase.loadTabs() if (tagList.isNotEmpty() && readerTagsList != tagList) { updateReaderTagsList(tagList) @@ -146,12 +142,6 @@ class ReaderViewModel @Inject constructor( _uiState.value = ContentUiState( tabUiStates = tagList.map { TabUiState(label = UiStringText(it.label)) }, selectedReaderTag = selectedReaderTag(), - searchMenuItemUiState = MenuItemUiState(isVisible = isSearchSupported()), - settingsMenuItemUiState = MenuItemUiState( - isVisible = isSettingsSupported(), - showQuickStartFocusPoint = - currentContentUiState?.settingsMenuItemUiState?.showQuickStartFocusPoint ?: false - ) ) if (!initialized) { initialized = true @@ -221,16 +211,6 @@ class ReaderViewModel @Inject constructor( } } - @Suppress("UseCheckOrError") - fun onSettingsActionClicked() { - if (isSettingsSupported()) { - completeQuickStartFollowSiteTaskIfNeeded() - _showSettings.value = Event(Unit) - } else if (BuildConfig.DEBUG) { - throw IllegalStateException("Settings should be hidden when isSettingsSupported returns false.") - } - } - @Suppress("unused", "UNUSED_PARAMETER") @Subscribe(threadMode = MAIN) fun onTagsUpdated(event: ReaderEvents.FollowedTagsChanged) { @@ -253,7 +233,6 @@ class ReaderViewModel @Inject constructor( readerTracker.stop(MAIN_READER) wasPaused = true if (!isChangingConfigurations) { - hideQuickStartFocusPointIfNeeded() dismissQuickStartSnackbarIfNeeded() if (quickStartRepository.isPendingTask(getFollowSiteTask())) { quickStartRepository.clearPendingTask() @@ -281,31 +260,12 @@ class ReaderViewModel @Inject constructor( } fun onQuickStartEventReceived(event: QuickStartEvent) { - if (event.task == getFollowSiteTask()) checkAndStartQuickStartFollowSiteTaskNextStep() + if (event.task == getFollowSiteTask()) startQuickStartFollowSiteTask() } - private fun checkAndStartQuickStartFollowSiteTaskNextStep() { - val isDiscover = appPrefsWrapper.getReaderTag()?.isDiscover == true - if (isDiscover) { - startQuickStartFollowSiteTaskDiscoverTabStep() - } else { - autoSwitchToDiscoverTab() - } - } - - private fun autoSwitchToDiscoverTab() { - launch { - if (!initialized) delay(QUICK_START_DISCOVER_TAB_STEP_DELAY) - readerTagsList.find { it.isDiscover }?.let { - updateSelectedContent(it) - } - startQuickStartFollowSiteTaskDiscoverTabStep() - } - } - - private fun startQuickStartFollowSiteTaskDiscoverTabStep() { + private fun startQuickStartFollowSiteTask() { val shortMessagePrompt = if (isSettingsSupported()) { - R.string.quick_start_dialog_follow_sites_message_short_discover_and_settings + R.string.quick_start_dialog_follow_sites_message_short_discover_and_subscriptions } else { R.string.quick_start_dialog_follow_sites_message_short_discover } @@ -314,16 +274,15 @@ class ReaderViewModel @Inject constructor( QuickStartReaderPrompt( getFollowSiteTask(), shortMessagePrompt, - R.drawable.ic_cog_white_24dp + QuickStartUtils.ICON_NOT_SET, ) ) - updateContentUiState(showQuickStartFocusPoint = isSettingsSupported()) + completeQuickStartFollowSiteTaskIfNeeded() } - fun completeQuickStartFollowSiteTaskIfNeeded() { + private fun completeQuickStartFollowSiteTaskIfNeeded() { if (quickStartRepository.isPendingTask(getFollowSiteTask())) { selectedSiteRepository.getSelectedSite()?.let { - hideQuickStartFocusPointIfNeeded() quickStartRepository.completeTask(getFollowSiteTask()) } } @@ -334,30 +293,9 @@ class ReaderViewModel @Inject constructor( isQuickStartPromptShown = false } - private fun hideQuickStartFocusPointIfNeeded() { - val currentUiState = _uiState.value as? ContentUiState - if (currentUiState?.settingsMenuItemUiState?.showQuickStartFocusPoint == true) { - updateContentUiState(showQuickStartFocusPoint = false) - } - } - private fun getFollowSiteTask() = quickStartRepository.quickStartType.getTaskFromString(QuickStartStore.QUICK_START_FOLLOW_SITE_LABEL) - private fun updateContentUiState( - showQuickStartFocusPoint: Boolean - ) { - val currentUiState = _uiState.value as? ContentUiState - currentUiState?.let { - _uiState.value = currentUiState.copy( - settingsMenuItemUiState = it.settingsMenuItemUiState.copy( - isVisible = isSettingsSupported(), - showQuickStartFocusPoint = showQuickStartFocusPoint - ), - ) - } - } - private fun selectedReaderTag(): ReaderTag? = _topBarUiState.value?.let { readerTagsList[readerTopBarMenuHelper.getReaderTagIndexFromMenuItem(it.selectedItem)] @@ -561,30 +499,19 @@ class ReaderViewModel @Inject constructor( } sealed class ReaderUiState( - open val searchMenuItemUiState: MenuItemUiState, - open val settingsMenuItemUiState: MenuItemUiState, val appBarExpanded: Boolean = false, val tabLayoutVisible: Boolean = false ) { data class ContentUiState( val tabUiStates: List, val selectedReaderTag: ReaderTag?, - override val searchMenuItemUiState: MenuItemUiState, - override val settingsMenuItemUiState: MenuItemUiState, ) : ReaderUiState( - searchMenuItemUiState = searchMenuItemUiState, - settingsMenuItemUiState = settingsMenuItemUiState, appBarExpanded = true, tabLayoutVisible = true ) { data class TabUiState( val label: UiString ) - - data class MenuItemUiState( - val isVisible: Boolean, - val showQuickStartFocusPoint: Boolean = false - ) } } @@ -596,7 +523,6 @@ class ReaderViewModel @Inject constructor( ) companion object { - private const val QUICK_START_DISCOVER_TAB_STEP_DELAY = 2000L private const val QUICK_START_PROMPT_DURATION = 5000 private const val FILTER_UPDATE_DELAY = 50L diff --git a/WordPress/src/main/res/layout/reader_home_settings_menu_action_layout.xml b/WordPress/src/main/res/layout/reader_home_settings_menu_action_layout.xml deleted file mode 100644 index 6f0bed3826f6..000000000000 --- a/WordPress/src/main/res/layout/reader_home_settings_menu_action_layout.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - diff --git a/WordPress/src/main/res/menu/reader_home.xml b/WordPress/src/main/res/menu/reader_home.xml deleted file mode 100644 index 3e225ff3f08d..000000000000 --- a/WordPress/src/main/res/menu/reader_home.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 1c2a647c5c95..d28bdcc5957d 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2241,9 +2241,9 @@ Add tags here to find posts about your favorite topics No subscribed tags No recommended blogs - @string/reader_filter_empty_blogs_list_title + @string/reader_filter_empty_blogs_list_title No blogs matching your search - @string/reader_filter_empty_blogs_list_text + @string/reader_filter_empty_blogs_list_text Subscribe to blogs in Discover or search for a blog that you like already. No recent posts The blogs you\'re subscribed to haven\'t posted anything recently @@ -3287,7 +3287,7 @@ Enable post sharing @string/quick_start_list_follow_site_subtitle Select %1$s Reader %2$s to find other sites. - Use <b> Discover </b> to find sites and tags. Try selecting %1$s Settings %2$s to add topics you like. + Use <b>Discover</b> to find sites and tags. Try selecting <b>Subscriptions</b> to view subscribed content and manage your subscriptions. Use <b> Discover </b> to find sites and tags. Select %1$s plus %2$s to upload media. You can add it to your posts/ pages from any device. Connect with other sites diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModelTest.kt index 1943a6a2f15a..d098e17f6477 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/discover/interests/ReaderInterestsViewModelTest.kt @@ -320,20 +320,6 @@ class ReaderInterestsViewModelTest : BaseUnitTest() { .isEqualTo(false) } - @Test - fun `when interest tag is toggled, then complete follow site quick start task if needed is invoked`() = - testWithEmptyUserTags { - val interests = getInterests() - whenever(readerTagRepository.getInterests()).thenReturn(SuccessWithData(interests)) - val selectInterestAtIndex = 2 - - initViewModel() - viewModel.onInterestAtIndexToggled(index = selectInterestAtIndex, isChecked = true) - - // Then - verify(parentViewModel).completeQuickStartFollowSiteTaskIfNeeded() - } - @Test fun `done button shown in enabled state if an interest is in selected state`() = testWithEmptyUserTags { diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt index 3cb4f933e28e..0b57e79121fc 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderViewModelTest.kt @@ -38,6 +38,7 @@ import org.wordpress.android.ui.reader.utils.DateProvider import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.QuickStartReaderPrompt import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.ReaderUiState.ContentUiState +import org.wordpress.android.ui.reader.viewmodels.ReaderViewModel.TopBarUiState import org.wordpress.android.util.JetpackBrandingUtils import org.wordpress.android.util.SnackbarSequencer import org.wordpress.android.viewmodel.Event @@ -119,7 +120,7 @@ class ReaderViewModelTest : BaseUnitTest() { // Arrange whenever(appPrefsWrapper.readerTagsUpdatedTimestamp).thenReturn(-1) // Act - triggerReaderTabContentDisplay() + triggerContentDisplay() // Assert assertThat(viewModel.updateTags.value?.getContentIfNotHandled()).isNotNull } @@ -129,7 +130,7 @@ class ReaderViewModelTest : BaseUnitTest() { // Arrange whenever(appPrefsWrapper.readerTagsUpdatedTimestamp).thenReturn(DUMMY_CURRENT_TIME - UPDATE_TAGS_THRESHOLD + 1) // Act - triggerReaderTabContentDisplay() + triggerContentDisplay() // Assert assertThat(viewModel.updateTags.value?.getContentIfNotHandled()).isNull() } @@ -139,7 +140,7 @@ class ReaderViewModelTest : BaseUnitTest() { // Arrange whenever(appPrefsWrapper.readerTagsUpdatedTimestamp).thenReturn(DUMMY_CURRENT_TIME - UPDATE_TAGS_THRESHOLD - 1) // Act - triggerReaderTabContentDisplay() + triggerContentDisplay() // Assert assertThat(viewModel.updateTags.value?.getContentIfNotHandled()).isNotNull } @@ -153,7 +154,7 @@ class ReaderViewModelTest : BaseUnitTest() { } whenever(loadReaderTabsUseCase.loadTabs()).thenReturn(ReaderTagList()) // Act - triggerReaderTabContentDisplay() + triggerContentDisplay() // Assert assertThat(state).isNull() } @@ -166,7 +167,7 @@ class ReaderViewModelTest : BaseUnitTest() { state = it } // Act - triggerReaderTabContentDisplay() + triggerContentDisplay() // Assert assertThat(state).isInstanceOf(ContentUiState::class.java) } @@ -282,74 +283,30 @@ class ReaderViewModelTest : BaseUnitTest() { @Test fun `Search is disabled for self-hosted login`() = testWithNonEmptyTags { // Arrange - var state: ReaderUiState? = null - viewModel.uiState.observeForever { + var state: TopBarUiState? = null + viewModel.topBarUiState.observeForever { state = it } // Act - triggerReaderTabContentDisplay(hasAccessToken = false) + triggerContentDisplay(hasAccessToken = false) // Assert - assertThat(state!!.searchMenuItemUiState.isVisible).isFalse + assertThat(state!!.isSearchActionVisible).isFalse } @Test fun `Search is enabled for dot com login`() = testWithNonEmptyTags { // Arrange whenever(accountStore.hasAccessToken()).thenReturn(true) - var state: ReaderUiState? = null - viewModel.uiState.observeForever { - state = it - } - // Act - triggerReaderTabContentDisplay() - - // Assert - assertThat(state!!.searchMenuItemUiState.isVisible).isTrue - } - - @Test - fun `OnSettingsActionClicked emits showSettings event`() { - // Arrange - whenever(accountStore.hasAccessToken()).thenReturn(true) - var event: Event? = null - viewModel.showSettings.observeForever { - event = it - } - // Act - viewModel.onSettingsActionClicked() - - // Assert - assertThat(event).isNotNull - } - - @Test - fun `Settings menu is disabled for self-hosted login`() = testWithNonEmptyTags { - // Arrange - var state: ReaderUiState? = null - viewModel.uiState.observeForever { - state = it - } - // Act - triggerReaderTabContentDisplay(hasAccessToken = false) - - // Assert - assertThat(state!!.settingsMenuItemUiState.isVisible).isFalse - } - - @Test - fun `Settings menu is enabled for dot com login`() = testWithNonEmptyTags { - // Arrange - whenever(accountStore.hasAccessToken()).thenReturn(true) - var state: ReaderUiState? = null - viewModel.uiState.observeForever { + var state: TopBarUiState? = null + viewModel.topBarUiState.observeForever { state = it } // Act - triggerReaderTabContentDisplay() + triggerContentDisplay() // Assert - assertThat(state!!.settingsMenuItemUiState.isVisible).isTrue + assertThat(state!!.isSearchActionVisible).isTrue } @Test @@ -360,7 +317,7 @@ class ReaderViewModelTest : BaseUnitTest() { uiStates.add(it) } // Act - triggerReaderTabContentDisplay() + triggerContentDisplay() // Assert assertThat(uiStates.size).isEqualTo(1) assertThat(uiStates[0]).isInstanceOf(ContentUiState::class.java) @@ -375,7 +332,7 @@ class ReaderViewModelTest : BaseUnitTest() { uiStates.add(it) } // Act - triggerReaderTabContentDisplay() + triggerContentDisplay() // Assert assertThat(uiStates.size).isEqualTo(1) assertThat(uiStates[0]).isInstanceOf(ContentUiState::class.java) @@ -440,59 +397,49 @@ class ReaderViewModelTest : BaseUnitTest() { } @Test - fun `given discover selected with settings available, when qs event follow site, then discover tab step started`() { + fun `given reader selected, when qs event follow site, then qs task started and completed`() { val tagList = createNonMockedNonEmptyReaderTagList() testWithNonMockedNonEmptyTags(tagList) { + whenever(selectedSiteRepository.getSelectedSite()).thenReturn(mock()) + whenever(quickStartRepository.isPendingTask(QuickStartNewSiteTask.FOLLOW_SITE)).thenReturn(true) + val observers = initObservers() - triggerReaderTabContentDisplay(selectedTabReaderTag = tagList[1], hasAccessToken = true) + triggerContentDisplay(hasAccessToken = true) viewModel.onQuickStartEventReceived(QuickStartEvent(QuickStartNewSiteTask.FOLLOW_SITE)) - assertQsFollowSiteDiscoverTabStepStarted(observers, isSettingsSupported = true) + assertQsFollowSiteTaskStarted(observers, isSettingsSupported = true) + assertQsFollowSiteTaskCompleted() } } @Test - fun `given discover selected no settings available, when qs event follow site, then discover tab step started`() { + fun `given reader selected no settings available, when qs event follow site, then qs task started and completed`() { val tagList = createNonMockedNonEmptyReaderTagList() testWithNonMockedNonEmptyTags(tagList) { + whenever(selectedSiteRepository.getSelectedSite()).thenReturn(mock()) + whenever(quickStartRepository.isPendingTask(QuickStartNewSiteTask.FOLLOW_SITE)).thenReturn(true) + val observers = initObservers() - triggerReaderTabContentDisplay(selectedTabReaderTag = tagList[1], hasAccessToken = false) + triggerContentDisplay(hasAccessToken = false) viewModel.onQuickStartEventReceived(QuickStartEvent(QuickStartNewSiteTask.FOLLOW_SITE)) - assertQsFollowSiteDiscoverTabStepStarted(observers, isSettingsSupported = false) + assertQsFollowSiteTaskStarted(observers, isSettingsSupported = false) + assertQsFollowSiteTaskCompleted() } } @Test - fun `given discover tab selected, when quick start event not follow site, then qs discover tab step not started`() { + fun `given reader selected, when quick start event not follow site, then qs task not started`() { val tagList = createNonMockedNonEmptyReaderTagList() testWithNonMockedNonEmptyTags(tagList) { val observers = initObservers() - triggerReaderTabContentDisplay(selectedTabReaderTag = tagList[1]) + triggerContentDisplay() viewModel.onQuickStartEventReceived(QuickStartEvent(QuickStartNewSiteTask.CHECK_STATS)) - assertQsFollowSiteDiscoverTabStepNotStarted(observers) - } - } - - /* QUICK START - SETTING MENU CLICK */ - - @Test - fun `given pending follow site qs task, when settings menu clicked, then qs follow site task is completed`() { - val tagList = createNonMockedNonEmptyReaderTagList() - testWithNonMockedNonEmptyTags(tagList) { - whenever(accountStore.hasAccessToken()).thenReturn(true) - whenever(selectedSiteRepository.getSelectedSite()).thenReturn(mock()) - whenever(quickStartRepository.isPendingTask(QuickStartNewSiteTask.FOLLOW_SITE)).thenReturn(true) - val observers = initObservers() - triggerReaderTabContentDisplay(selectedTabReaderTag = tagList[1]) - - viewModel.onSettingsActionClicked() - - assertQsFollowSiteTaskCompleted(observers) + assertQsFollowSiteTaskNotStarted(observers) } } @@ -549,41 +496,33 @@ class ReaderViewModelTest : BaseUnitTest() { assertThat(showJetpackOverlayEvent.last().peekContent()).isTrue } - private fun assertQsFollowSiteDiscoverTabStepStarted( + private fun assertQsFollowSiteTaskStarted( observers: Observers, isSettingsSupported: Boolean = true ) { with(observers) { assertThat(quickStartReaderPrompts.last().peekContent().shortMessagePrompt).isEqualTo( if (isSettingsSupported) { - R.string.quick_start_dialog_follow_sites_message_short_discover_and_settings + R.string.quick_start_dialog_follow_sites_message_short_discover_and_subscriptions } else { R.string.quick_start_dialog_follow_sites_message_short_discover } ) - assertThat(uiStates.last().findSettingsMenuQsFocusPoint()).isEqualTo(isSettingsSupported) } } - private fun assertQsFollowSiteDiscoverTabStepNotStarted( + private fun assertQsFollowSiteTaskNotStarted( observers: Observers ) { with(observers) { assertThat(quickStartReaderPrompts).isEmpty() - assertThat(uiStates.last().findSettingsMenuQsFocusPoint()).isEqualTo(false) } } - private fun assertQsFollowSiteTaskCompleted( - observers: Observers - ) { + private fun assertQsFollowSiteTaskCompleted() { verify(quickStartRepository).completeTask(QuickStartNewSiteTask.FOLLOW_SITE) - assertThat(observers.uiStates.last().findSettingsMenuQsFocusPoint()).isEqualTo(false) } - private fun ReaderUiState.findSettingsMenuQsFocusPoint() = - (this as? ContentUiState)?.settingsMenuItemUiState?.showQuickStartFocusPoint ?: false - private fun initObservers(): Observers { val uiStates = mutableListOf() viewModel.uiState.observeForever { @@ -610,11 +549,9 @@ class ReaderViewModelTest : BaseUnitTest() { val tabNavigationEvents: List ) - private fun triggerReaderTabContentDisplay( - selectedTabReaderTag: ReaderTag? = null, + private fun triggerContentDisplay( hasAccessToken: Boolean = true ) { - whenever(appPrefsWrapper.getReaderTag()).thenReturn(selectedTabReaderTag) whenever(accountStore.hasAccessToken()).thenReturn(hasAccessToken) viewModel.start() } diff --git a/fastlane/resources/values/strings.xml b/fastlane/resources/values/strings.xml index 4c38e8df0c9c..d28bdcc5957d 100644 --- a/fastlane/resources/values/strings.xml +++ b/fastlane/resources/values/strings.xml @@ -958,6 +958,10 @@ Design System Foundation Components + Colors + Fonts + Lengths + DSButton Stats @@ -1280,6 +1284,10 @@ Weeks Months Years + By day + By week + By month + By year Last 7-days Previous 7-days Views @@ -1630,9 +1638,21 @@ Reader Filter - Sites - Topics - Following + Filter by blog + Filter by tag + See the newest posts from blogs you\'re subscribed to + You can subscribe to posts on a specific subject by adding a tag + Log in to WordPress.com to see the latest posts from blogs you\'re subscribed to + Log in to WordPress.com to see the latest posts from tags you\'re subscribed to + Subscribe to a blog + Add a tag + No blog subscriptions + Subscribe to blogs in Discover and you’ll see their latest posts here. Or search for a blog that you like already. + No tags + Subscribe to a tag and you’ll be able to see the best posts from it here. + Search for a blog + Suggested tags + Subscribe to a tag Log in to WordPress.com \u0020\u2022\u0020 There was a problem handling the request. Please try again later. @@ -1640,12 +1660,16 @@ +%1$s Hide You might like + Blogs to subscribe to You might like By %1$s more items Welcome! + Subscribe to tags to discover new blogs No recent posts + Try subscribing to more tags to broaden the search Get Started + Subscribe to tags %1$s ▸ %2$s No response received Invalid response received @@ -1665,6 +1689,19 @@ Marked post as seen Marked post as unseen Can\'t toggle seen status of this post + Search + Discover + Subscriptions + Saved + Liked + Automattic + Lists + 0 Blogs + 1 Blog + %d Blogs + 0 Tags + 1 Tag + %d Tags Post saved online @@ -2044,6 +2081,7 @@ Saved Discover Likes + Subscribed now @@ -2056,21 +2094,22 @@ Application log - Reader Site - Topic + Reader Blog + Tag Reader Post Related Post - Manage Topics & Sites + Manage Tags & Blogs %1$d of %2$d @string/comments + Follow tags - Followed topics - Followed sites + Subscribed tags + Subscribed blogs - Edit topics and sites - Block this site + Edit tags and blogs + Block this blog Report this post Report this user Mark as seen @@ -2079,14 +2118,17 @@ Share + Follow + Subscribe + Subscribed Save Saved Add to saved posts Remove from saved posts View All Post saved - Turn off site notifications - Turn on site notifications + Turn off blog notifications + Turn on blog notifications Select a few to continue Done Follow conversation by email @@ -2095,9 +2137,8 @@ Remove the current filter - Select a Site or Tag to filter posts - Select a Tag or Site, Pop Up Window - Choose your topics + Select a Tag or Blog, Pop Up Window + Choose your tags Enable notifications for %1$s%2$s%3$s? Enable @@ -2106,27 +2147,27 @@ Reply to post… Reply to comment… - Enter a URL or topic to follow + Enter a URL or tag to subscribe to Search WordPress - Search followed sites + Search subscribed blogs New posts Tap to show them Added %s Removed %s - Site followed + Blog subscribed Comments are closed View original article - %,d Followers - %s followers - 1 follower + %,d Subscribers + %s subscribers + 1 subscriber Load more posts What would you like to find? More from %s More on WordPress.com Visit site - Choose your topics + Choose your interests View comments Welcome to Reader. Discover millions of blogs at your fingertips. Back @@ -2134,9 +2175,9 @@ Reblog - No available WordPress.com sites - Once you create a WordPress.com site, you can reblog content that you like to your own site. - Manage Sites + No available WordPress.com blogs + Once you create a WordPress.com blog, you can reblog content that you like to your own site. + Manage Blogs Reblog failed Continue @@ -2157,20 +2198,20 @@ Couldn\'t post your comment - You already follow this topic - That isn\'t a valid topic - Unable to add this topic - Unable to remove this topic + You\'re already subscribed to this tag + That isn\'t a valid tag + Unable to add this tag + Unable to remove this tag Unable to share Unable to view image - Unable to show this site - You already follow this site - Unable to follow this site - This site could not be found - You are not authorized to access this site - Unable to unfollow this site - Posts from this site will no longer be shown - Unable to block this site + Unable to show this blog + You are already subscribed to this blog + Unable to subscribe to this blog + This blog could not be found + You are not authorized to access this blog + Unable to unsubscribe from blog + Posts from this blog will no longer be shown + Unable to block this blog Posts from this user will no longer be shown Unable to perform this action Failed to like this post @@ -2194,19 +2235,20 @@ @string/no_network_title Unable to retrieve posts Use the filter button to find posts on specific subjects - No posts with this topic + No posts with this tag Fetching posts… - The sites in this list haven\'t posted anything recently - Add topics here to find posts about your favorite topics - No followed topics - No recommended sites - No followed sites - No sites matching your search - When you follow sites, you\'ll see their content here + The blogs in this list have not posted anything recently + Add tags here to find posts about your favorite topics + No subscribed tags + No recommended blogs + @string/reader_filter_empty_blogs_list_title + No blogs matching your search + @string/reader_filter_empty_blogs_list_text + Subscribe to blogs in Discover or search for a blog that you like already. No recent posts - The sites you follow haven\'t posted anything recently - Discover sites - Go to following + The blogs you\'re subscribed to haven\'t posted anything recently + Discover blogs + Go to subscriptions Nothing liked yet Posts that you like will appear here No posts saved — yet! @@ -2218,7 +2260,7 @@ View all comments Be the first to comment Follow Conversation - This site is empty + This blog is empty No results found No results found for %s for your language Unable to perform search @@ -3245,7 +3287,7 @@ Enable post sharing @string/quick_start_list_follow_site_subtitle Select %1$s Reader %2$s to find other sites. - Use <b> Discover </b> to find sites and tags. Try selecting %1$s Settings %2$s to add topics you like. + Use <b>Discover</b> to find sites and tags. Try selecting <b>Subscriptions</b> to view subscribed content and manage your subscriptions. Use <b> Discover </b> to find sites and tags. Select %1$s plus %2$s to upload media. You can add it to your posts/ pages from any device. Connect with other sites @@ -4785,14 +4827,14 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> - Scan Barcode - Camera permission is required to scan the barcode. - Grant Camera Permission - Camera permission is required in order to scan the barcode - You have permanently denied Camera permission. It is required in order to scan the barcode. Please enable it from the app settings - Grant - Cancel - Go to settings + Scan Barcode + Camera permission is required to scan the barcode. + Grant Camera Permission + Camera permission is required in order to scan the barcode + You have permanently denied Camera permission. It is required in order to scan the barcode. Please enable it from the app settings + Grant + Cancel + Go to settings Use a security key There was some trouble with the Security key login @@ -4830,4 +4872,10 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> Working Offline Waiting for connection + + Site Monitoring + Metrics + PHP Logs + Web Server Logs + We cannot open site monitoring at the moment. Please try again later diff --git a/version.properties b/version.properties index 2d7244af2f5d..bebb7d297a3d 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ -versionName=24.1 -versionCode=1406 \ No newline at end of file +versionName=24.2-rc-1 +versionCode=1407 \ No newline at end of file