Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow a post to be marked as sticky #15351

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1b9b0ad
Add a (dummy) Switch to toggle if the post is sticky in Post Settings
dnalves Sep 18, 2021
11a960f
[PostSettings] Add logic to the "sticky" switch to update the PostModel
dnalves Sep 19, 2021
0d0c7c0
[PostSettings] Hide "sticky" switch when is a page (and not a post)
dnalves Sep 19, 2021
44eac7a
Update RELEASE-NOTES
dnalves Sep 20, 2021
e1a896f
Update fluxCVersion to PR #2126
dnalves Sep 20, 2021
82abfb7
[PostSettings] Rename Sticky switch listener and extract styles
dnalves Sep 21, 2021
41f52e0
Merge branch 'develop' into add-sticky-switch-to-post-settings
dnalves Sep 21, 2021
8dc8842
Update RELEASE-NOTES.txt
dnalves Sep 21, 2021
b268a43
Update build.gradle to point fluxCVersion to the correct PR-commit
dnalves Sep 21, 2021
a69566f
Change FluxC version to the latest develop
dnalves Sep 21, 2021
043caa8
Display 'Sticky' label in the Post List for sticky posts
dnalves Sep 21, 2021
b0a811f
Fix detekt and klint for PostListItemUiStateHelper
dnalves Sep 21, 2021
982bd0f
A few code improvements in PostListItemUiStateHelper
dnalves Sep 22, 2021
985ed1f
Refactor PostPageListLabelColorUseCase to use the Post instead of mul…
dnalves Sep 24, 2021
9f8175b
Change color of the sticky label to same as other info labels (yellow)
dnalves Sep 24, 2021
5d5989c
Update FluxC version to the latest develop commit
dnalves Sep 24, 2021
c2893e8
Merge remote-tracking branch 'upstream/develop' into add-sticky-switc…
dnalves Sep 24, 2021
ff5191b
Add unit tests for Sticky label color use case
dnalves Sep 24, 2021
283f040
Merge remote-tracking branch 'upstream/develop' into add-sticky-switc…
dnalves Oct 5, 2021
c1a6a81
Update FluxC version to the latest develop
dnalves Oct 5, 2021
71b9e4f
Merge branch 'develop' into add-sticky-switch-to-post-settings
jd-alexander Oct 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Expand Up @@ -2,6 +2,7 @@

18.5
-----
* [*] Allow users to mark Posts as sticky [https://github.com/wordpress-mobile/WordPress-Android/pull/15351]
* [*] Fixed a crash in Page Template chooser [https://github.com/wordpress-mobile/WordPress-Android/pull/15415]

18.4
Expand Down
Expand Up @@ -13,13 +13,15 @@
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.SwitchCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
Expand Down Expand Up @@ -116,6 +118,7 @@ public class EditPostSettingsFragment extends Fragment {
private ImageView mFeaturedImageView;
private ImageView mLocalFeaturedImageView;
private Button mFeaturedImageButton;
private SwitchCompat mStickySwitch;
private ViewGroup mFeaturedImageRetryOverlay;
private ViewGroup mFeaturedImageProgressOverlay;

Expand All @@ -139,6 +142,9 @@ public class EditPostSettingsFragment extends Fragment {
@Inject ViewModelProvider.Factory mViewModelFactory;
private EditPostPublishSettingsViewModel mPublishedViewModel;

private final OnCheckedChangeListener mOnStickySwitchChangeListener =
(buttonView, isChecked) -> onStickySwitchChanged(isChecked);


public interface EditPostActivityHook {
EditPostRepository getEditPostRepository();
Expand Down Expand Up @@ -258,6 +264,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
mFeaturedImageHeaderTextView = rootView.findViewById(R.id.post_settings_featured_image_header);
mPublishHeaderTextView = rootView.findViewById(R.id.post_settings_publish);
mPublishDateContainer = rootView.findViewById(R.id.publish_date_container);
mStickySwitch = rootView.findViewById(R.id.post_settings_sticky_switch);

mFeaturedImageView = rootView.findViewById(R.id.post_featured_image);
mLocalFeaturedImageView = rootView.findViewById(R.id.post_featured_image_local);
Expand Down Expand Up @@ -355,13 +362,17 @@ public void onClick(View view) {
}
});

mStickySwitch.setOnCheckedChangeListener(mOnStickySwitchChangeListener);


if (getEditPostRepository() != null && getEditPostRepository().isPage()) { // remove post specific views
final View categoriesTagsContainer = rootView.findViewById(R.id.post_categories_and_tags_card);
final View formatBottomSeparator = rootView.findViewById(R.id.post_format_bottom_separator);
final View markAsStickyContainer = rootView.findViewById(R.id.post_settings_mark_as_sticky_container);
categoriesTagsContainer.setVisibility(View.GONE);
formatBottomSeparator.setVisibility(View.GONE);
mFormatContainer.setVisibility(View.GONE);
markAsStickyContainer.setVisibility(View.GONE);
}

mPublishedViewModel.getOnUiModel().observe(getViewLifecycleOwner(), new Observer<PublishUiModel>() {
Expand Down Expand Up @@ -470,6 +481,7 @@ public void refreshViews() {
mPublishedViewModel.start(getEditPostRepository());
updateCategoriesTextView(postModel);
updateFeaturedImageView(postModel);
updateStickySwitch(postModel);
}

@Override
Expand Down Expand Up @@ -564,6 +576,16 @@ private void showTagsActivity() {
startActivityForResult(tagsIntent, ACTIVITY_REQUEST_CODE_SELECT_TAGS);
}

private void onStickySwitchChanged(boolean checked) {
EditPostRepository editPostRepository = getEditPostRepository();
if (editPostRepository != null) {
editPostRepository.updateAsync(postModel -> {
postModel.setSticky(checked);
return true;
}, null);
}
}

/*
* called by the activity when the user taps OK on a PostSettingsDialogFragment
*/
Expand Down Expand Up @@ -840,6 +862,17 @@ private void updateTagsTextView(PostImmutableModel postModel) {
mTagsTextView.setText(tags);
}

private void updateStickySwitch(PostImmutableModel postModel) {
if (!isAdded() || postModel == null || mStickySwitch == null) {
return;
}

// We need to remove the listener first, otherwise the listener will be triggered
mStickySwitch.setOnCheckedChangeListener(null);
mStickySwitch.setChecked(postModel.getSticky());
mStickySwitch.setOnCheckedChangeListener(mOnStickySwitchChangeListener);
}

private void updatePostFormatTextView(PostImmutableModel postModel) {
// Post format can be updated due to a site settings fetch and the textView might not have been initialized yet
if (mPostFormatTextView == null) {
Expand Down
Expand Up @@ -25,31 +25,9 @@ class PostPageListLabelColorUseCase @Inject constructor() {
hasUnhandledConflicts: Boolean,
hasUnhandledAutoSave: Boolean
): Int? {
return getLabelColor(
PostStatus.fromPost(post),
post.isLocalDraft,
post.isLocallyChanged,
uploadUiState,
hasUnhandledConflicts,
hasUnhandledAutoSave
)
}

@ColorRes private fun getLabelColor(
postStatus: PostStatus,
isLocalDraft: Boolean,
isLocallyChanged: Boolean,
uploadUiState: PostUploadUiState,
hasUnhandledConflicts: Boolean,
hasAutoSave: Boolean
): Int? {
val isError = (uploadUiState is UploadFailed && !uploadUiState.isEligibleForAutoUpload) ||
hasUnhandledConflicts
val isProgressInfo = uploadUiState is UploadingPost || uploadUiState is UploadingMedia ||
uploadUiState is UploadQueued
val isStateInfo = (uploadUiState is UploadFailed && uploadUiState.isEligibleForAutoUpload) ||
isLocalDraft || isLocallyChanged || postStatus == PRIVATE || postStatus == PENDING ||
uploadUiState is UploadWaitingForConnection || hasAutoSave
val isError = isError(uploadUiState, hasUnhandledConflicts)
val isProgressInfo = isProgressInfo(uploadUiState)
val isStateInfo = isStateInfoState(uploadUiState, post, hasUnhandledAutoSave)

return when {
isError -> ERROR_COLOR
Expand All @@ -58,4 +36,26 @@ class PostPageListLabelColorUseCase @Inject constructor() {
else -> null
}
}

private fun isError(
uploadUiState: PostUploadUiState,
hasUnhandledConflicts: Boolean
) = (uploadUiState is UploadFailed && !uploadUiState.isEligibleForAutoUpload) ||
hasUnhandledConflicts

private fun isProgressInfo(uploadUiState: PostUploadUiState) =
uploadUiState is UploadingPost || uploadUiState is UploadingMedia ||
uploadUiState is UploadQueued

private fun isStateInfoState(
uploadUiState: PostUploadUiState,
post: PostModel,
hasUnhandledAutoSave: Boolean
): Boolean {
val postStatus = PostStatus.fromPost(post)

return (uploadUiState is UploadFailed && uploadUiState.isEligibleForAutoUpload) ||
post.isLocalDraft || post.isLocallyChanged || postStatus == PRIVATE || postStatus == PENDING ||
post.sticky || uploadUiState is UploadWaitingForConnection || hasUnhandledAutoSave
}
}
Expand Up @@ -115,8 +115,7 @@ class PostListItemUiStateHelper @Inject constructor(
)
val statuses = getStatuses(
postStatus = postStatus,
isLocalDraft = post.isLocalDraft,
isLocallyChanged = post.isLocallyChanged,
post = post,
uploadUiState = uploadUiState,
hasUnhandledConflicts = unhandledConflicts,
hasAutoSave = hasAutoSave
Expand Down Expand Up @@ -232,17 +231,15 @@ class PostListItemUiStateHelper @Inject constructor(
uploadUiState is UploadQueued
}

private fun getStatuses(
postStatus: PostStatus,
isLocalDraft: Boolean,
isLocallyChanged: Boolean,
private fun getErrorAndProgressStatuses(
uploadUiState: PostUploadUiState,
postStatus: PostStatus,
hasUnhandledConflicts: Boolean,
hasAutoSave: Boolean
): List<UiString> {
): MutableList<UiString> {
val labels: MutableList<UiString> = ArrayList()
when {
uploadUiState is PostUploadUiState.UploadFailed -> {
uploadUiState is UploadFailed -> {
getErrorLabel(uploadUiState, postStatus)?.let { labels.add(it) }
}
uploadUiState is UploadingPost -> if (uploadUiState.isDraft) {
Expand All @@ -253,28 +250,53 @@ class PostListItemUiStateHelper @Inject constructor(
uploadUiState is UploadingMedia -> labels.add(UiStringRes(R.string.uploading_media))
uploadUiState is UploadQueued -> labels.add(UiStringRes(R.string.post_queued))
uploadUiState is UploadWaitingForConnection -> {
when (uploadUiState.postStatus) {
UNKNOWN, PUBLISHED -> labels.add(UiStringRes(R.string.post_waiting_for_connection_publish))
PRIVATE -> labels.add(UiStringRes(R.string.post_waiting_for_connection_private))
PENDING -> labels.add(UiStringRes(R.string.post_waiting_for_connection_pending))
SCHEDULED -> labels.add(UiStringRes(R.string.post_waiting_for_connection_scheduled))
DRAFT -> labels.add(UiStringRes(R.string.post_waiting_for_connection_draft))
TRASHED -> AppLog.e(
POSTS,
"Developer error: This state shouldn't happen. Trashed post is in " +
"UploadWaitingForConnection state."
)
getWaitingForConnectionStatus(uploadUiState.postStatus)?.let {
labels.add(it)
}
}
hasUnhandledConflicts -> labels.add(UiStringRes(R.string.local_post_is_conflicted))
hasAutoSave -> labels.add(UiStringRes(R.string.local_post_autosave_revision_available))
}
return labels
}

private fun getWaitingForConnectionStatus(postStatus: PostStatus): UiString? {
return when (postStatus) {
UNKNOWN, PUBLISHED -> (UiStringRes(R.string.post_waiting_for_connection_publish))
PRIVATE -> (UiStringRes(R.string.post_waiting_for_connection_private))
PENDING -> (UiStringRes(R.string.post_waiting_for_connection_pending))
SCHEDULED -> (UiStringRes(R.string.post_waiting_for_connection_scheduled))
DRAFT -> (UiStringRes(R.string.post_waiting_for_connection_draft))
TRASHED -> {
AppLog.e(
POSTS,
"Developer error: This state shouldn't happen. Trashed post is in " +
"UploadWaitingForConnection state."
)
return null
}
}
}

private fun getStatuses(
postStatus: PostStatus,
post: PostModel,
uploadUiState: PostUploadUiState,
hasUnhandledConflicts: Boolean,
hasAutoSave: Boolean
): List<UiString> {
val labels = getErrorAndProgressStatuses(
uploadUiState,
postStatus,
hasUnhandledConflicts,
hasAutoSave
)

// we want to show either single error/progress label or 0-n info labels.
if (labels.isEmpty()) {
if (isLocalDraft) {
if (post.isLocalDraft) {
labels.add(UiStringRes(R.string.local_draft))
} else if (isLocallyChanged) {
} else if (post.isLocallyChanged) {
labels.add(UiStringRes(R.string.local_changes))
}
if (postStatus == PRIVATE) {
Expand All @@ -283,6 +305,9 @@ class PostListItemUiStateHelper @Inject constructor(
if (postStatus == PENDING) {
labels.add(UiStringRes(R.string.post_status_pending_review))
}
if (post.sticky) {
labels.add(UiStringRes(R.string.post_status_sticky))
}
}
return labels
}
Expand Down
21 changes: 21 additions & 0 deletions WordPress/src/main/res/layout/edit_post_settings_fragment.xml
Expand Up @@ -220,6 +220,27 @@

</LinearLayout>

<!-- Mark as Sticky -->

<LinearLayout
android:id="@+id/post_settings_mark_as_sticky_container"
style="@style/PostSettingsCardViewInnerLayout" >

<com.google.android.material.textview.MaterialTextView
style="@style/PostSettingsSectionHeader"
android:text="@string/post_settings_mark_as_sticky_options_header" />

<LinearLayout style="@style/PostSettingsContainer">

<androidx.appcompat.widget.SwitchCompat
android:id="@+id/post_settings_sticky_switch"
style="@style/PostSettingsSwitch"
android:text="@string/post_settings_post_sticky" />

</LinearLayout>

</LinearLayout>

<!--More Options-->

<LinearLayout style="@style/PostSettingsCardViewInnerLayout">
Expand Down
3 changes: 3 additions & 0 deletions WordPress/src/main/res/values/strings.xml
Expand Up @@ -1586,12 +1586,14 @@
<string name="post_settings_status" translatable="false">@string/status_and_visibility</string>
<string name="post_settings_categories_and_tags">Categories &amp; Tags</string>
<string name="post_settings_publish">Publish</string>
<string name="post_settings_mark_as_sticky_options_header">Mark as sticky</string>
<string name="post_settings_more_options">More Options</string>
<string name="post_settings_not_set">Not Set</string>
<string name="post_settings_excerpt">Excerpt</string>
<string name="post_settings_slug">Slug</string>
<string name="post_settings_tags">Tags</string>
<string name="post_settings_post_format">Post Format</string>
<string name="post_settings_post_sticky">Stick post to the front page</string>
<string name="post_settings_featured_image">Featured Image</string>
<string name="post_settings_set_featured_image">Set Featured Image</string>
<string name="post_settings_featured_image_uploading_tap_for_options">Uploading media.\nPlease tap for options.</string>
Expand Down Expand Up @@ -1635,6 +1637,7 @@
<string name="post_status_publish_post">Publish</string>
<string name="post_status_pending_review">Pending review</string>
<string name="post_status_draft">Draft</string>
<string name="post_status_sticky">Sticky</string>
<string name="post_status_post_private">Private</string>
<string name="post_status_post_scheduled">Scheduled</string>
<string name="post_status_post_trashed">Trashed</string>
Expand Down
7 changes: 7 additions & 0 deletions WordPress/src/main/res/values/styles.xml
Expand Up @@ -824,6 +824,13 @@
<item name="android:background">?android:listDivider</item>
</style>

<style name="PostSettingsSwitch">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">?attr/textAppearanceSubtitle1</item>
</style>

<!--Quick Start Prompt Dialog Styles -->

<style name="QuickStartPromptDialogTitleNew">
Expand Down