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

Editor - Move Undo/Redo to the header #18705

Merged
merged 22 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,42 @@ class BlockEditorTests : BaseTest() {
.switchToVisualMode()
.verifyPostElementText(mPostText)
}

@Test
fun e2eBlockEditorCanUndoChanges() {
val title = "blockEditorCanUndoChanges"
MySitesPage()
.startNewPost()
BlockEditorPage()
.waitForTitleDisplayed()
.enterTitle(title)
.enterParagraphText(mPostText)
.verifyContentStructure(1, mPostText.split(" ").count(), mPostText.length)
.dismissContentStructure()
.undo()
.undo()
.verifyContentStructure(0, 0, 0)
.dismissContentStructure()
}

@Test
fun e2eBlockEditorCanRedoChanges() {
val title = "blockEditorCanRedoChanges"
MySitesPage()
.startNewPost()
BlockEditorPage()
.waitForTitleDisplayed()
.enterTitle(title)
.enterParagraphText(mPostText)
.verifyContentStructure(1, mPostText.split(" ").count(), mPostText.length)
.dismissContentStructure()
.undo()
.undo()
.verifyContentStructure(0, 0, 0)
.dismissContentStructure()
.redo()
.redo()
.verifyContentStructure(1, mPostText.split(" ").count(), mPostText.length)
.dismissContentStructure()
}
tiagomar marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ class BlockEditorPage {
return this
}

fun dismissContentStructure(): BlockEditorPage {
Espresso.pressBack()
return this
}
tiagomar marked this conversation as resolved.
Show resolved Hide resolved

fun addPostSettings(categoryName: String?, tagName: String?): BlockEditorPage {
openPostSetting()
addCategory(categoryName)
Expand Down Expand Up @@ -194,6 +199,35 @@ class BlockEditorPage {
WPSupportUtils.clickOn(Espresso.onView(ViewMatchers.withText(R.string.menu_preview)))
}

fun verifyContentStructure(blocks: Int, words: Int, characters: Int): BlockEditorPage {
val mContentStructure = "Blocks: %1\$d\nWords: %2\$d\nCharacters: %3\$d"

Espresso.openActionBarOverflowOrOptionsMenu(ApplicationProvider.getApplicationContext())
WPSupportUtils.clickOn(Espresso.onView(ViewMatchers.withText("Content structure")))

TestCase.assertTrue(
"Expected content structure is not valid",
WPSupportUtils.waitForElementToBeDisplayedWithoutFailure(
Espresso.onView(
ViewMatchers.withText(
String.format(mContentStructure, blocks, words, characters)
)
)
)
)
return this
}

fun undo(): BlockEditorPage {
WPSupportUtils.clickOn((R.id.menu_undo_action))
return this
}

fun redo(): BlockEditorPage {
WPSupportUtils.clickOn((R.id.menu_redo_action))
return this
}

companion object {
private val titleField = Espresso.onView(ViewMatchers.withHint("Add title"))
private val postSettingButton =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
import android.os.Handler;
import android.text.TextUtils;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.activity.OnBackPressedCallback;
Expand All @@ -26,6 +30,7 @@
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback;
import androidx.core.content.ContextCompat;
import androidx.core.util.Consumer;
import androidx.core.util.Pair;
import androidx.fragment.app.Fragment;
Expand Down Expand Up @@ -216,7 +221,9 @@
import org.wordpress.android.util.extensions.AppBarLayoutExtensionsKt;
import org.wordpress.android.util.helpers.MediaFile;
import org.wordpress.android.util.helpers.MediaGallery;
import org.wordpress.android.util.image.BlavatarShape;
import org.wordpress.android.util.image.ImageManager;
import org.wordpress.android.util.image.ImageType;
import org.wordpress.android.viewmodel.helpers.ToastMessageHolder;
import org.wordpress.android.viewmodel.storage.StorageUtilsViewModel;
import org.wordpress.android.widgets.AppRatingDialog;
Expand Down Expand Up @@ -304,6 +311,8 @@ public class EditPostActivity extends LocaleAwareActivity implements
private static final String STATE_KEY_EDITOR_SESSION_DATA = "stateKeyEditorSessionData";
private static final String STATE_KEY_GUTENBERG_IS_SHOWN = "stateKeyGutenbergIsShown";
private static final String STATE_KEY_MEDIA_CAPTURE_PATH = "stateKeyMediaCapturePath";
private static final String STATE_KEY_UNDO = "stateKeyUndo";
private static final String STATE_KEY_REDO = "stateKeyRedo";

private static final int PAGE_CONTENT = 0;
private static final int PAGE_SETTINGS = 1;
Expand Down Expand Up @@ -368,6 +377,9 @@ enum RestartEditorOptions {

private AppBarLayout mAppBarLayout;
private Toolbar mToolbar;
private Menu mMenu;
private boolean mMenuHasUndo = false;
private boolean mMenuHasRedo = false;

private Handler mShowPrepublishingBottomSheetHandler;
private Runnable mShowPrepublishingBottomSheetRunnable;
Expand Down Expand Up @@ -585,9 +597,12 @@ public void handleOnBackPressed() {
// Set up the action bar.
mToolbar = findViewById(R.id.toolbar_main);
setSupportActionBar(mToolbar);

customizeToolbar();

final ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(false);
}

mAppBarLayout = findViewById(R.id.appbar_main);
Expand Down Expand Up @@ -751,7 +766,7 @@ public void handleOnBackPressed() {
resetUploadingMediaToFailedIfPostHasNotMediaInProgressOrQueued();
}

setTitle(SiteUtils.getSiteNameOrHomeURL(mSite));
getSupportActionBar().setDisplayShowTitleEnabled(false);

mSectionsPagerAdapter = new SectionsPagerAdapter(fragmentManager);

Expand All @@ -774,6 +789,35 @@ public void handleOnBackPressed() {
mStorageUtilsViewModel.start(savedInstanceState == null);
}

private void customizeToolbar() {
// Custom overflow icon
Drawable overflowIcon = ContextCompat.getDrawable(this, R.drawable.more_vertical);
mToolbar.setOverflowIcon(overflowIcon);

// Customize insets
int insetStart = (int) (8 * getResources().getDisplayMetrics().density);
mToolbar.setContentInsetsRelative(insetStart, mToolbar.getContentInsetEnd());

// Set custom header
LayoutInflater inflater = LayoutInflater.from(this);
View customLayout = inflater.inflate(R.layout.edit_post_header, mToolbar, false);
mToolbar.addView(customLayout, 0);

Button closeButton = customLayout.findViewById(R.id.close_editor_button);
closeButton.setOnClickListener(v -> onBackPressed());

// Update site icon
String siteIconUrl = SiteUtils.getSiteIconUrl(
mSite,
getResources().getDimensionPixelSize(R.dimen.blavatar_sz_small)
);
ImageButton siteIcon = customLayout.findViewById(R.id.close_editor_site_icon);
ImageType blavatarType = SiteUtils.getSiteImageType(
mSite.isWpForTeamsSite(), BlavatarShape.SQUARE_WITH_ROUNDED_CORNERES);
mImageManager.loadImageWithCorners(siteIcon, blavatarType, siteIconUrl,
DisplayUtils.dpToPx(siteIcon.getContext(), 2));
}

private void presentNewPageNoticeIfNeeded() {
if (!mIsPage || !mIsNewPost) {
return;
Expand Down Expand Up @@ -1082,6 +1126,8 @@ protected void onSaveInstanceState(Bundle outState) {
mIsConfigChange = true; // don't call sessionData.end() in onDestroy() if this is an Android config change

outState.putBoolean(STATE_KEY_GUTENBERG_IS_SHOWN, mShowGutenbergEditor);
outState.putBoolean(STATE_KEY_UNDO, mMenuHasUndo);
outState.putBoolean(STATE_KEY_REDO, mMenuHasRedo);

outState.putParcelableArrayList(STATE_KEY_DROPPED_MEDIA_URIS, mEditorMedia.getDroppedMediaUris());

Expand Down Expand Up @@ -1265,6 +1311,42 @@ public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.edit_post, menu);
mMenu = menu;
boolean isRtlLayout = getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;

// Set opacity for undo/redo items
MenuItem undoItem = mMenu.findItem(R.id.menu_undo_action);
MenuItem redoItem = mMenu.findItem(R.id.menu_redo_action);

if (undoItem.getActionView() != null) {
ImageView undoIcon = undoItem.getActionView().findViewById(R.id.menu_undo_icon);
undoIcon.setRotationY(isRtlLayout ? 180 : 0);
}

if (redoItem.getActionView() != null) {
ImageView redoIcon = redoItem.getActionView().findViewById(R.id.menu_redo_icon);
redoIcon.setRotationY(isRtlLayout ? 180 : 0);
}

undoItem.setEnabled(mMenuHasUndo);
View undoView = undoItem.getActionView();
ImageView undoIcon = undoView.findViewById(R.id.menu_undo_icon);
undoIcon.setImageAlpha(mMenuHasUndo ? 255 : 76);
undoView.setOnClickListener(v -> {
if (mEditorFragment instanceof GutenbergEditorFragment) {
((GutenbergEditorFragment) mEditorFragment).onUndoPressed();
}
});

redoItem.setEnabled(mMenuHasRedo);
View redoView = redoItem.getActionView();
ImageView redoIcon = redoView.findViewById(R.id.menu_redo_icon);
redoIcon.setImageAlpha(mMenuHasRedo ? 255 : 76);
redoView.setOnClickListener(v -> {
if (mEditorFragment instanceof GutenbergEditorFragment) {
((GutenbergEditorFragment) mEditorFragment).onRedoPressed();
}
});
return true;
}

Expand Down Expand Up @@ -3518,6 +3600,40 @@ public void onTrackableEvent(TrackableEvent event, Map<String, String> propertie
AnalyticsUtils.trackBlockEditorEvent(eventName, mSite, properties);
}

@Override public void onToggleUndo(boolean isDisabled) {
mMenuHasUndo = !isDisabled;
if (mMenu != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
MenuItem undoItem = mMenu.findItem(R.id.menu_undo_action);
undoItem.setEnabled(mMenuHasUndo);

View undoView = undoItem.getActionView();
ImageView undoIcon = undoView.findViewById(R.id.menu_undo_icon);
undoIcon.setImageAlpha(mMenuHasUndo ? 255 : 76);
}
});
}
}

@Override public void onToggleRedo(boolean isDisabled) {
mMenuHasRedo = !isDisabled;
if (mMenu != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
MenuItem redoItem = mMenu.findItem(R.id.menu_redo_action);
redoItem.setEnabled(mMenuHasRedo);

View redoView = redoItem.getActionView();
ImageView redoIcon = redoView.findViewById(R.id.menu_redo_icon);
redoIcon.setImageAlpha(mMenuHasRedo ? 255 : 76);
}
});
}
}

// FluxC events

@SuppressWarnings("unused")
Expand Down
10 changes: 10 additions & 0 deletions WordPress/src/main/res/drawable/chevron_down.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorOnSurface">
<path
android:pathData="M17.5,11.6L12,16L6.5,11.6L7.4,10.4L12,14L16.5,10.4L17.5,11.6Z"
android:fillColor="?attr/colorOnSurface"/>
</vector>
9 changes: 9 additions & 0 deletions WordPress/src/main/res/drawable/more_vertical.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M13,19H11V17H13V19ZM13,13H11V11H13V13ZM13,7H11V5H13V7Z"
android:fillColor="?attr/colorOnSurface"/>
</vector>
10 changes: 10 additions & 0 deletions WordPress/src/main/res/drawable/redo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorOnSurface">
<path
android:pathData="M15.6,6.5L14.5,7.5L17.4,10.8H8C7.1,10.8 6.3,11.1 5.7,11.7C4.3,13.2 4.3,15.9 4.3,17.3V17.5H5.8V17.2C5.8,16.1 5.8,13.7 6.8,12.7C7.1,12.4 7.5,12.2 8.1,12.2H17.3L14.5,15L15.6,16.1L20.2,11.5L15.6,6.5Z"
android:fillColor="?attr/colorOnSurface"/>
</vector>
10 changes: 10 additions & 0 deletions WordPress/src/main/res/drawable/undo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorOnSurface">
<path
android:pathData="M18.3,11.7C17.7,11.1 16.9,10.8 16,10.8H6.7L9.6,7.5L8.5,6.5L4,11.5L8.5,16L9.5,15L6.8,12.3H16C16.5,12.3 16.9,12.5 17.3,12.8C18.3,13.8 18.3,16.2 18.3,17.3V17.6H19.8V17.4C19.8,15.9 19.8,13.1 18.3,11.7Z"
android:fillColor="?attr/colorOnSurface"/>
</vector>
51 changes: 51 additions & 0 deletions WordPress/src/main/res/layout/edit_post_header.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true">

<ImageButton
android:id="@+id/close_editor"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignBottom="@id/close_editor_button"
android:layout_alignParentStart="true"
android:layout_alignTop="@id/close_editor_button"
android:background="@null"
android:importantForAccessibility="no"
android:src="@drawable/chevron_down"
geriux marked this conversation as resolved.
Show resolved Hide resolved
android:clickable="false"
android:visibility="visible" />

<androidx.cardview.widget.CardView
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_toEndOf="@id/close_editor"
android:layout_centerVertical="true"
android:layout_marginStart="2dp"
app:cardElevation="0dp">

<ImageButton
android:id="@+id/close_editor_site_icon"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:contentDescription="@string/menu_site_icon_context"
android:src="@drawable/bg_rectangle_placeholder_globe_32dp"
android:visibility="visible"
tools:ignore="TouchTargetSizeCheck" />

</androidx.cardview.widget.CardView>

<Button
android:id="@+id/close_editor_button"
android:layout_width="57dp"
android:layout_height="57dp"
android:layout_centerVertical="true"
android:background="?android:attr/actionBarItemBackground"
android:contentDescription="@string/menu_close_editor"
android:textColor="@android:color/transparent" />

</RelativeLayout>
Loading