From 55f86e46cc7c1e71198d4fac5b26de6b9e82fe80 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 3 May 2022 23:45:04 +1000 Subject: [PATCH 01/25] WIP Sync page JS rewrite. --- assets/js/sync/components/common/log.js | 53 +++ .../js/sync/components/common/progress-bar.js | 30 ++ assets/js/sync/components/common/status.js | 34 ++ assets/js/sync/components/icons/pause.js | 15 + assets/js/sync/components/icons/play.js | 15 + assets/js/sync/components/icons/stop.js | 15 + .../js/sync/components/icons/thumbs-down.js | 15 + assets/js/sync/components/icons/thumbs-up.js | 15 + assets/js/sync/components/sync-button.js | 49 ++ assets/js/sync/components/sync-controls.js | 49 ++ assets/js/sync/components/sync-log.js | 26 ++ assets/js/sync/components/sync-progress.js | 62 +++ assets/js/sync/components/sync-status.js | 32 ++ assets/js/sync/config.js | 22 + assets/js/sync/context.js | 22 + assets/js/sync/hooks.js | 128 ++++++ assets/js/sync/index.js | 435 ++++++++++++++++++ assets/js/sync/utilities.js | 36 ++ images/pause.svg | 3 - images/resume.svg | 3 - images/stop.svg | 3 - images/thumbsdown.svg | 3 - images/thumbsup.svg | 3 - includes/classes/Screen/Sync.php | 9 +- includes/partials/sync-page.php | 199 +------- package.json | 2 +- 26 files changed, 1063 insertions(+), 215 deletions(-) create mode 100644 assets/js/sync/components/common/log.js create mode 100644 assets/js/sync/components/common/progress-bar.js create mode 100644 assets/js/sync/components/common/status.js create mode 100644 assets/js/sync/components/icons/pause.js create mode 100644 assets/js/sync/components/icons/play.js create mode 100644 assets/js/sync/components/icons/stop.js create mode 100644 assets/js/sync/components/icons/thumbs-down.js create mode 100644 assets/js/sync/components/icons/thumbs-up.js create mode 100644 assets/js/sync/components/sync-button.js create mode 100644 assets/js/sync/components/sync-controls.js create mode 100644 assets/js/sync/components/sync-log.js create mode 100644 assets/js/sync/components/sync-progress.js create mode 100644 assets/js/sync/components/sync-status.js create mode 100644 assets/js/sync/config.js create mode 100644 assets/js/sync/context.js create mode 100644 assets/js/sync/hooks.js create mode 100644 assets/js/sync/index.js create mode 100644 assets/js/sync/utilities.js delete mode 100644 images/pause.svg delete mode 100644 images/resume.svg delete mode 100644 images/stop.svg delete mode 100644 images/thumbsdown.svg delete mode 100644 images/thumbsup.svg diff --git a/assets/js/sync/components/common/log.js b/assets/js/sync/components/common/log.js new file mode 100644 index 0000000000..18a488a976 --- /dev/null +++ b/assets/js/sync/components/common/log.js @@ -0,0 +1,53 @@ +/** + * WordPress dependencies. + */ +import { TabPanel } from '@wordpress/components'; +import { useMemo, WPElement } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; + +/** + * Log component. + * + * @param {object} props Component props. + * @param {object[]} props.messages Log messages. + * @returns {WPElement} Component. + */ +export default ({ messages }) => { + const errorMessages = useMemo(() => messages.filter((m) => m.status === 'error'), [messages]); + + const tabs = [ + { + messages, + name: 'full', + title: __('Full Log', 'elasticpress'), + }, + { + messages: errorMessages, + name: 'error', + title: sprintf( + /* translators: %d: Error message count. */ + __('Errors (%d)', 'elasticpress'), + errorMessages.length, + ), + }, + ]; + + return ( + + {({ messages }) => ( +
+ {messages.map((m, i) => ( +
+ {i + 1} +
+ ))} + {messages.map((m) => ( +
+ {m.message} +
+ ))} +
+ )} +
+ ); +}; diff --git a/assets/js/sync/components/common/progress-bar.js b/assets/js/sync/components/common/progress-bar.js new file mode 100644 index 0000000000..0145e76572 --- /dev/null +++ b/assets/js/sync/components/common/progress-bar.js @@ -0,0 +1,30 @@ +/** + * WordPress dependencies. + */ +import { WPElement } from '@wordpress/element'; + +/** + * Progress bar component. + * + * @param {object} props Component props. + * @param {boolean} props.isComplete Is operation complete. + * @param {number} props.current Current value. + * @param {number} props.total Current total. + * @returns {WPElement} Component. + */ +export default ({ isComplete, current, total }) => { + const now = Math.floor((current / total) * 100); + + return ( +
+
+ {`${now}%`} +
+ ); +}; diff --git a/assets/js/sync/components/common/status.js b/assets/js/sync/components/common/status.js new file mode 100644 index 0000000000..3d83adcbe6 --- /dev/null +++ b/assets/js/sync/components/common/status.js @@ -0,0 +1,34 @@ +/** + * WordPress dependencies. + */ +import { WPElement } from '@wordpress/element'; +import { Icon } from '@wordpress/components'; +import { dateI18n } from '@wordpress/date'; + +/** + * Internal dependencies. + */ +import thumbsDown from '../icons/thumbs-down'; +import thumbsUp from '../icons/thumbs-up'; + +/** + * Sync button component. + * + * @param {object} props Component props. + * @param {string} props.dateTime Relevant date and time. + * @param {boolean} props.isSuccess Is the status success. + * @param {string} props.label Status label. + * @returns {WPElement} Component. + */ +export default ({ dateTime, isSuccess, label }) => { + return ( +
+ {label} + +
+ ); +}; diff --git a/assets/js/sync/components/icons/pause.js b/assets/js/sync/components/icons/pause.js new file mode 100644 index 0000000000..64c45c5334 --- /dev/null +++ b/assets/js/sync/components/icons/pause.js @@ -0,0 +1,15 @@ +import { SVG, Path } from '@wordpress/primitives'; + +export default () => { + return ( + + + + ); +}; diff --git a/assets/js/sync/components/icons/play.js b/assets/js/sync/components/icons/play.js new file mode 100644 index 0000000000..9052734fd3 --- /dev/null +++ b/assets/js/sync/components/icons/play.js @@ -0,0 +1,15 @@ +import { SVG, Path } from '@wordpress/primitives'; + +export default () => { + return ( + + + + ); +}; diff --git a/assets/js/sync/components/icons/stop.js b/assets/js/sync/components/icons/stop.js new file mode 100644 index 0000000000..027bd0998b --- /dev/null +++ b/assets/js/sync/components/icons/stop.js @@ -0,0 +1,15 @@ +import { SVG, Path } from '@wordpress/primitives'; + +export default () => { + return ( + + + + ); +}; diff --git a/assets/js/sync/components/icons/thumbs-down.js b/assets/js/sync/components/icons/thumbs-down.js new file mode 100644 index 0000000000..7677253ea0 --- /dev/null +++ b/assets/js/sync/components/icons/thumbs-down.js @@ -0,0 +1,15 @@ +import { SVG, Path } from '@wordpress/primitives'; + +export default () => { + return ( + + + + ); +}; diff --git a/assets/js/sync/components/icons/thumbs-up.js b/assets/js/sync/components/icons/thumbs-up.js new file mode 100644 index 0000000000..3e76177d50 --- /dev/null +++ b/assets/js/sync/components/icons/thumbs-up.js @@ -0,0 +1,15 @@ +import { SVG, Path } from '@wordpress/primitives'; + +export default () => { + return ( + + + + ); +}; diff --git a/assets/js/sync/components/sync-button.js b/assets/js/sync/components/sync-button.js new file mode 100644 index 0000000000..30dc2a4a07 --- /dev/null +++ b/assets/js/sync/components/sync-button.js @@ -0,0 +1,49 @@ +import { Button } from '@wordpress/components'; +import { useContext, WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { external, update } from '@wordpress/icons'; + +/** + * Internal dependencies. + */ +import { SyncContext } from '../context'; + +/** + * Sync button component. + * + * @param {object} props Component props. + * @param {boolean} props.isDelete Whether button is a delete button. + * @param {Function} props.onClick Click callback. + * @returns {WPElement} Component. + */ +export default ({ isDelete, onClick }) => { + const { isSyncing } = useContext(SyncContext); + + /** + * Render. + */ + return ( + <> + + {!isDelete && ( + + )} + + ); +}; diff --git a/assets/js/sync/components/sync-controls.js b/assets/js/sync/components/sync-controls.js new file mode 100644 index 0000000000..b7f2ff4c06 --- /dev/null +++ b/assets/js/sync/components/sync-controls.js @@ -0,0 +1,49 @@ +/** + * WordPress dependencies. + */ +import { Button } from '@wordpress/components'; +import { useContext, WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies. + */ +import { SyncContext } from '../context'; +import pause from './icons/pause'; +import play from './icons/play'; +import stop from './icons/stop'; + +/** + * Sync button component. + * + * @param {object} props Component props. + * @param {Function} props.onPlayPause Play/pause button click callback. + * @param {Function} props.onStop Stop button click callback. + * @returns {WPElement} Component. + */ +export default ({ onPlayPause, onStop }) => { + const { isPaused } = useContext(SyncContext); + + /** + * Render. + */ + return ( + <> + - - - - -
- - - - - -
-
- -
-
-
- " /> -
-
-
-
-
- - - - -
-
- - - -
-
-
- -
-
- -
-
-
-
-
-
-
-
-
-
-
- - - -
-

- -

-
-
-

- -

-
-
- - - - -
- - - - - -
-
-
-
-
- " /> -
-
-
-
-
- - - - -
-
- - - -
-
-
- -
-
- -
-
-
-
-
-
-
-
-
-
-
-
- " - /> -

- -

-
-
-
- +
diff --git a/package.json b/package.json index 2e5517091b..4e8f213675 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "ordering-script.min": "./assets/js/ordering/index.js", "related-posts-block-script.min": "./assets/js/blocks/related-posts/block.js", "settings-script.min": "./assets/js/settings.js", - "sync-script.min": "./assets/js/sync.js", + "sync-script.min": "./assets/js/sync/index.js", "sites-admin-script.min": "./assets/js/sites-admin.js", "stats-script.min": "./assets/js/stats.js", "synonyms-script.min": "./assets/js/synonyms/index.js", From 3d5a8c73531c67212251a01bcf4309074052a422 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 00:14:36 +1000 Subject: [PATCH 02/25] Mild refactor of sync page JS to improve clarity. --- assets/css/sync.css | 347 +------------ assets/css/sync/button.css | 24 + assets/css/sync/controls.css | 16 + assets/css/sync/heading.css | 12 + assets/css/sync/messages.css | 25 + assets/css/sync/panel.css | 34 ++ assets/css/sync/progress-bar.css | 28 ++ assets/css/sync/progress.css | 52 ++ assets/css/sync/status.css | 16 + assets/css/sync/time.css | 5 + assets/js/sync/components/common/date-time.js | 20 + assets/js/sync/components/common/log.js | 53 -- .../js/sync/components/common/message-log.js | 35 ++ .../js/sync/components/common/progress-bar.js | 11 +- assets/js/sync/components/common/status.js | 34 -- assets/js/sync/components/icons/sync.js | 15 + assets/js/sync/components/sync-button.js | 49 -- assets/js/sync/components/sync-controls.js | 87 +++- assets/js/sync/components/sync-log.js | 26 - assets/js/sync/components/sync-logs.js | 72 +++ assets/js/sync/components/sync-progress.js | 63 ++- assets/js/sync/components/sync-status.js | 38 +- assets/js/sync/config.js | 22 +- assets/js/sync/context.js | 22 - assets/js/sync/hooks.js | 99 ++-- assets/js/sync/index.js | 471 +++++++++++------- assets/js/sync/utilities.js | 13 + includes/classes/Screen/Sync.php | 1 + package-lock.json | 8 +- package.json | 1 + 30 files changed, 883 insertions(+), 816 deletions(-) create mode 100644 assets/css/sync/button.css create mode 100644 assets/css/sync/controls.css create mode 100644 assets/css/sync/heading.css create mode 100644 assets/css/sync/messages.css create mode 100644 assets/css/sync/panel.css create mode 100644 assets/css/sync/progress-bar.css create mode 100644 assets/css/sync/progress.css create mode 100644 assets/css/sync/status.css create mode 100644 assets/css/sync/time.css create mode 100644 assets/js/sync/components/common/date-time.js delete mode 100644 assets/js/sync/components/common/log.js create mode 100644 assets/js/sync/components/common/message-log.js delete mode 100644 assets/js/sync/components/common/status.js create mode 100644 assets/js/sync/components/icons/sync.js delete mode 100644 assets/js/sync/components/sync-button.js delete mode 100644 assets/js/sync/components/sync-log.js create mode 100644 assets/js/sync/components/sync-logs.js delete mode 100644 assets/js/sync/context.js diff --git a/assets/css/sync.css b/assets/css/sync.css index f6cc9cd892..d259f74af5 100644 --- a/assets/css/sync.css +++ b/assets/css/sync.css @@ -1,334 +1,17 @@ -:root { - --ep-admin-color-base-white: #fff; - --ep-admin-color-blue-01: #00a0d2; - --ep-admin-color-grey-01: #f1f1f1; - --ep-admin-color-green-01: #46b450; - --ep-admin-color-red-01: #b52727; - --ep-admin-color-dark-01: #333; - --ep-admin-max-width: 1200px; - - --ep-admin-box-title: #1d2327; - - --ep-admin-delete-sync-button-bg-color: rgba(181, 39, 39, 0.03); - - --ep-admin-progress-bar-bg-color: rgba(0, 160, 210, 0.3); - - --ep-admin-output-tab-color: #1e1e1e; - - --ep-admin-log-bg-color: #1a1e24; - --ep-admin-log-line-number-color: #999; - --ep-admin-log-line-number-bg-color: #303030; - -} - -.elasticpress_page_elasticpress-sync .button:disabled { - cursor: not-allowed; -} - -.ep-sync-box__progress-wrapper { - display: none; -} - -.ep-sync-box__output { - background-color: var(--ep-admin-log-bg-color); - display: none; - margin-bottom: 20px; - max-height: 200px; - overflow-y: scroll; - position: relative; -} - -.ep-sync-box__output_active { - display: block; -} - -.ep-sync-box__output-wrapper { - color: var(--ep-admin-color-base-white); - margin-left: 30px; - min-height: 200px; -} - -.ep-sync-box__output-line { - position: relative; -} - -.ep-sync-box__output-line-number { - background-color: var(--ep-admin-log-line-number-bg-color); - color: var(--ep-admin-log-line-number-color); - left: -30px; - min-width: 20px; - padding: 0 3px 0 5px; - position: absolute; - text-align: right; - white-space: nowrap; -} - -.ep-sync-box__output-line-text { - font-size: 12px; - padding-left: 14px; -} - -.ep-sync-box__output-tabs { - align-items: center; - display: flex; -} - -.ep-sync-box__output-tabs_hide { - display: none; -} - -.ep-sync-box__output-tab { - color: var(--ep-admin-output-tab-color); - padding: 16px; -} - -.ep-sync-box__output-tab:hover { - cursor: pointer; -} - -.ep-sync-box__output-tab_active { - border-bottom: 4px solid var(--ep-admin-color-blue-01); - padding-bottom: 12px; -} - -.ep-sync-box__output-tab_active:hover { - cursor: default; -} - -.ep-sync-box__button-text { - height: 21px; -} - -.elasticpress_page_elasticpress-sync .card { - max-width: var(--ep-admin-max-width); - - & .ep-sync-box__description-actions { - display: flex; - flex-direction: column; - - @media (min-width: 768px) { - flex-direction: row; - justify-content: space-between; - } - } - - & .ep-sync-box__description { +@import "sync/button.css"; +@import "sync/controls.css"; +@import "sync/heading.css"; +@import "sync/messages.css"; +@import "sync/panel.css"; +@import "sync/progress.css"; +@import "sync/progress-bar.css"; +@import "sync/status.css"; +@import "sync/time.css"; - @media (min-width: 768px) { - width: 69%; - } - } - - & .ep-sync-box__action { - align-items: center; - display: flex; - flex-direction: column; - margin-top: 40px; - - @media (min-width: 768px) { - width: 30%; - } - - & .ep-sync-box__button { - align-items: center; - display: flex; - font-size: 24px; - font-weight: 700; - height: 68px; - justify-content: space-between; - padding: 20px 40px; - width: 228px; - } - - & .ep-sync-box__icon-button { - font-size: 28px; - height: 28px; - width: 28px; - - } - - & .ep-sync-box__learn-more-link { - margin-top: 19px; - } - - } - - & .ep-sync-box__description_text { - font-size: 18px; - } - - & .ep-last-sync { - margin-bottom: 12px; - } - - & .ep-last-sync__title { - font-size: 20px; - font-weight: 700; - margin-bottom: 0.5em; - } - - & .ep-last-sync__icon-status { - margin-right: 5px; - vertical-align: text-bottom; - } - - & .ep-last-sync__date { - background-color: var(--ep-admin-color-grey-01); - padding: 6px; - } - - & .ep-sync-box__buttons { - display: flex; - - } - - & .ep-sync-box__button-resume, - & .ep-sync-box__button-pause, - & .ep-sync-box__button-stop { - align-items: center; - display: none; - flex-direction: column; - height: 68px; - justify-content: center; - width: 100px; - } - - & .ep-sync-box__button-stop { - background-color: var(--ep-admin-color-blue-01); - border-color: var(--ep-admin-color-blue-01); - margin-left: 24px; - } - - & .ep-sync-box__progress { - align-items: normal; - display: flex; - flex-direction: column; - margin-bottom: 5px; - margin-top: 19px; - - @media (min-width: 768px) { - align-items: center; - flex-direction: row; - } - } - - & .ep-sync-box__sync-in-progress { - display: flex; - flex-direction: row; - - @media (min-width: 768px) { - width: 30%; - } - } - - & .ep-sync-box__progressbar { - background-color: var(--ep-admin-color-grey-01); - border-radius: 24px; - height: 20px; - margin: 15px 0; - position: relative; - width: 100%; - - @media (min-width: 768px) { - margin: 0; - width: 65%; - } - } - - & .ep-sync-box__progressbar_animated { - background-color: var(--ep-admin-progress-bar-bg-color); - color: var(--ep-admin-color-dark-01); - display: block; - height: 100%; - margin: 0; - text-align: center; - transition: width 0.5s ease-in-out; - width: 0; - } - - & .ep-sync-box__progressbar_complete { - background-color: var(--ep-admin-color-green-01); - color: var(--ep-admin-color-base-white); - } - - & .ep-sync-box__sync-in-progress-info { - display: flex; - flex-direction: column; - margin-left: 12px; - } - - & .ep-sync-box__progress-info { - font-size: 14px; - font-weight: 500; - margin-bottom: 5px; - } - - & .ep-sync-box__start-time { - color: var(--ep-admin-color-blue-01); - font-size: 14px; - } - - & .ep-sync-box__start-time-date { - color: var(--ep-admin-color-dark-01); - } -} - -.elasticpress_page_elasticpress-sync .ep-delete-data-and-sync { - margin-top: 40px; - - & .card { - margin-top: 17px; - } - - & .ep-delete-data-and-sync__title { - color: var(--ep-admin-box-title); - font-size: 18px; - font-weight: 400; - } - - & .ep-delete-data-and-sync__warning { - align-items: flex-start; - display: flex; - - @media (min-width: 768px) { - width: 69%; - } - } - - & .ep-delete-data-and-sync__warning-icon { - margin-right: 9px; - margin-top: 17px; - } - - & .ep-delete-data-and-sync__button { - background-color: var(--ep-admin-delete-sync-button-bg-color); - border: 1px solid var(--ep-admin-color-red-01); - color: var(--ep-admin-color-red-01); - margin: 5px 0 12px; - } - - & .ep-delete-data-and-sync__button-cancel { - display: none; - } - - & .ep-sync-box__action { - flex-direction: column; - height: auto; - justify-content: center; - margin-top: 0; - width: 100%; - - @media (min-width: 768px) { - flex-direction: row; - height: 68px; - justify-content: space-between; - } - } - - & .ep-sync-box__buttons { - - @media (min-width: 768px) { - margin-right: 5%; - } - } +:root { + --ep-sync-color-black: #1a1e24; + --ep-sync-color-error: #b52727; + --ep-sync-color-light-grey: #f0f0f0; + --ep-sync-color-success: #46b450; + --ep-sync-color-white: #fff; } diff --git a/assets/css/sync/button.css b/assets/css/sync/button.css new file mode 100644 index 0000000000..9870395de4 --- /dev/null +++ b/assets/css/sync/button.css @@ -0,0 +1,24 @@ +.ep-sync-button { + height: 4rem; + width: 100%; + + &.components-button.has-icon.has-text { + justify-content: center; + + & svg { + height: 2em; + margin: 0; + width: 2em; + } + } +} + +.ep-sync-button--sync { + font-size: 1.5em; + font-weight: 700; +} + +.ep-sync-button--resume, +.ep-sync-button--stop { + flex-direction: column; +} diff --git a/assets/css/sync/controls.css b/assets/css/sync/controls.css new file mode 100644 index 0000000000..2a9b63eb1e --- /dev/null +++ b/assets/css/sync/controls.css @@ -0,0 +1,16 @@ +.ep-sync-controls { + display: grid; + grid-gap: 1em; + grid-template-columns: 1fr 1fr; + margin: 0 auto; + max-width: 16rem; +} + +.ep-sync-controls__sync { + grid-column: 1 / -1; +} + +.ep-sync-controls__learn-more { + grid-column: 1 / -1; + text-align: center; +} diff --git a/assets/css/sync/heading.css b/assets/css/sync/heading.css new file mode 100644 index 0000000000..6a3ceb84a5 --- /dev/null +++ b/assets/css/sync/heading.css @@ -0,0 +1,12 @@ +.ep-sync-heading { + + @nest .wrap & { + font-weight: 400; + margin: 0.5rem 0 0.75rem; + padding: 0; + + &h2 { + color: inherit; + } + } +} diff --git a/assets/css/sync/messages.css b/assets/css/sync/messages.css new file mode 100644 index 0000000000..58233f4d4d --- /dev/null +++ b/assets/css/sync/messages.css @@ -0,0 +1,25 @@ +.ep-sync-messages { + align-content: start; + background: var(--ep-sync-color-black); + color: var(--ep-sync-color-white); + display: grid; + font-family: monospace; + grid-auto-flow: column; + grid-template-columns: min-content auto; + height: 21em; + line-height: 2; + overflow-y: auto; + white-space: pre-wrap; +} + +.ep-sync-messages__message { + grid-column: 2; +} + +.ep-sync-messages__line-number { + box-sizing: content-box; + min-width: 3ch; + opacity: 0.5; + padding: 0 0.5em; + text-align: right; +} diff --git a/assets/css/sync/panel.css b/assets/css/sync/panel.css new file mode 100644 index 0000000000..88340dc2ea --- /dev/null +++ b/assets/css/sync/panel.css @@ -0,0 +1,34 @@ +.ep-sync-panel { + margin-bottom: 2rem; + max-width: 1200px; +} + +.ep-sync-panel__body { + display: grid; + grid-column-gap: 2rem; + grid-row-gap: 1rem; + grid-template-columns: auto 16rem; + + &.is-opened { + padding: 2rem 2rem 1rem 2rem; + } + + & p, + & .components-toggle-control, + & .components-tab-panel__tab-content { + margin-bottom: 1rem; + margin-top: 0; + } + + @media (max-width: 960px) { + grid-template-columns: 100%; + } +} + +.ep-sync-panel__row { + grid-column: 1 / -1; +} + +.ep-sync-panel__introduction { + font-size: 18px; +} diff --git a/assets/css/sync/progress-bar.css b/assets/css/sync/progress-bar.css new file mode 100644 index 0000000000..39e7c2dea1 --- /dev/null +++ b/assets/css/sync/progress-bar.css @@ -0,0 +1,28 @@ +.ep-sync-progress-bar { + background: var(--ep-sync-color-light-grey); + display: flex; + line-height: 1.75; + overflow: hidden; + text-align: center; +} + +.ep-sync-progress-bar, +.ep-sync-progress-bar__progress { + border-radius: 0.875em; +} + +.ep-sync-progress-bar__progress { + background: var(--wp-admin-theme-color); + color: var(--ep-sync-color-white); + padding: 0 0.875em; + transition: all 500ms ease-in-out; + white-space: nowrap; + + @nest .ep-sync-progress-bar--complete & { + background: var(--ep-sync-color-success); + } + + @nest .ep-sync-progress-bar--paused & { + opacity: 0.5; + } +} diff --git a/assets/css/sync/progress.css b/assets/css/sync/progress.css new file mode 100644 index 0000000000..e80a2533a0 --- /dev/null +++ b/assets/css/sync/progress.css @@ -0,0 +1,52 @@ +@keyframes epSyncRotation { + + from { + transform: rotate(0deg); + } + + to { + transform: rotate(359deg); + } +} + +.ep-sync-progress { + align-items: center; + display: grid; + grid-row-gap: 1rem; + grid-template-columns: min-content minmax(max-content, 1fr) 3fr; + margin-bottom: 1rem; + + @media (max-width: 960px) { + grid-template-columns: min-content auto; + } + + & svg { + animation: epSyncRotation 1500ms infinite linear; + animation-play-state: paused; + height: 36px; + margin-right: 12px; + width: 36px; + } +} + +.ep-sync-progress--syncing { + + & svg { + animation-play-state: running; + } +} + +.ep-sync-progress__details { + + & strong { + display: block; + font-size: 14px; + } +} + +.ep-sync-progress__progress-bar { + + @media (max-width: 960px) { + grid-column: 1 / -1; + } +} diff --git a/assets/css/sync/status.css b/assets/css/sync/status.css new file mode 100644 index 0000000000..481f452a64 --- /dev/null +++ b/assets/css/sync/status.css @@ -0,0 +1,16 @@ +.ep-sync-status { + align-items: center; + display: flex; + + & svg { + fill: var(--ep-sync-color-error); + margin-right: 0.5em; + } +} + +.ep-sync-status--success { + + & svg { + fill: var(--ep-sync-color-success); + } +} diff --git a/assets/css/sync/time.css b/assets/css/sync/time.css new file mode 100644 index 0000000000..bd65f25ca4 --- /dev/null +++ b/assets/css/sync/time.css @@ -0,0 +1,5 @@ +.ep-sync-time { + background-color: var(--ep-sync-color-light-grey); + border-radius: 2px; + padding: 0.25em 0.5em; +} diff --git a/assets/js/sync/components/common/date-time.js b/assets/js/sync/components/common/date-time.js new file mode 100644 index 0000000000..105d735bd9 --- /dev/null +++ b/assets/js/sync/components/common/date-time.js @@ -0,0 +1,20 @@ +/** + * WordPress dependencies. + */ +import { dateI18n } from '@wordpress/date'; +import { WPElement } from '@wordpress/element'; + +/** + * Log component. + * + * @param {object} props Component props. + * @param {string} props.dateTime Date and time. + * @returns {WPElement} Component. + */ +export default ({ dateTime }) => { + return ( + + ); +}; diff --git a/assets/js/sync/components/common/log.js b/assets/js/sync/components/common/log.js deleted file mode 100644 index 18a488a976..0000000000 --- a/assets/js/sync/components/common/log.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * WordPress dependencies. - */ -import { TabPanel } from '@wordpress/components'; -import { useMemo, WPElement } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; - -/** - * Log component. - * - * @param {object} props Component props. - * @param {object[]} props.messages Log messages. - * @returns {WPElement} Component. - */ -export default ({ messages }) => { - const errorMessages = useMemo(() => messages.filter((m) => m.status === 'error'), [messages]); - - const tabs = [ - { - messages, - name: 'full', - title: __('Full Log', 'elasticpress'), - }, - { - messages: errorMessages, - name: 'error', - title: sprintf( - /* translators: %d: Error message count. */ - __('Errors (%d)', 'elasticpress'), - errorMessages.length, - ), - }, - ]; - - return ( - - {({ messages }) => ( -
- {messages.map((m, i) => ( -
- {i + 1} -
- ))} - {messages.map((m) => ( -
- {m.message} -
- ))} -
- )} -
- ); -}; diff --git a/assets/js/sync/components/common/message-log.js b/assets/js/sync/components/common/message-log.js new file mode 100644 index 0000000000..f555197d95 --- /dev/null +++ b/assets/js/sync/components/common/message-log.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies. + */ +import { WPElement } from '@wordpress/element'; + +/** + * Log component. + * + * @param {object} props Component props. + * @param {object[]} props.messages Log messages. + * @returns {WPElement} Component. + */ +export default ({ messages }) => { + return ( +
+ {messages.map((m, i) => ( +
+ {i + 1} +
+ ))} + {messages.map((m) => ( +
+ {m.message} +
+ ))} +
+ ); +}; diff --git a/assets/js/sync/components/common/progress-bar.js b/assets/js/sync/components/common/progress-bar.js index 0145e76572..f910580e5c 100644 --- a/assets/js/sync/components/common/progress-bar.js +++ b/assets/js/sync/components/common/progress-bar.js @@ -7,9 +7,9 @@ import { WPElement } from '@wordpress/element'; * Progress bar component. * * @param {object} props Component props. - * @param {boolean} props.isComplete Is operation complete. * @param {number} props.current Current value. * @param {number} props.total Current total. + * @param {boolean} props.isComplete If operation is complete. * @returns {WPElement} Component. */ export default ({ isComplete, current, total }) => { @@ -20,11 +20,14 @@ export default ({ isComplete, current, total }) => { aria-valuemax={100} aria-valuemin={0} aria-valuenow={now} - className={`ep-sync-progress-bar ${isComplete ? `ep-sync-progress-bar--complete` : ``}`} + className={`ep-sync-progress-bar ${isComplete ? `ep-sync-progress-bar--complete` : ``} + `} role="progressbar" > -
- {`${now}%`} +
{`${now}%`}
); }; diff --git a/assets/js/sync/components/common/status.js b/assets/js/sync/components/common/status.js deleted file mode 100644 index 3d83adcbe6..0000000000 --- a/assets/js/sync/components/common/status.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * WordPress dependencies. - */ -import { WPElement } from '@wordpress/element'; -import { Icon } from '@wordpress/components'; -import { dateI18n } from '@wordpress/date'; - -/** - * Internal dependencies. - */ -import thumbsDown from '../icons/thumbs-down'; -import thumbsUp from '../icons/thumbs-up'; - -/** - * Sync button component. - * - * @param {object} props Component props. - * @param {string} props.dateTime Relevant date and time. - * @param {boolean} props.isSuccess Is the status success. - * @param {string} props.label Status label. - * @returns {WPElement} Component. - */ -export default ({ dateTime, isSuccess, label }) => { - return ( -
- {label} - -
- ); -}; diff --git a/assets/js/sync/components/icons/sync.js b/assets/js/sync/components/icons/sync.js new file mode 100644 index 0000000000..e42ac958b3 --- /dev/null +++ b/assets/js/sync/components/icons/sync.js @@ -0,0 +1,15 @@ +import { SVG, Path } from '@wordpress/primitives'; + +export default () => { + return ( + + + + ); +}; diff --git a/assets/js/sync/components/sync-button.js b/assets/js/sync/components/sync-button.js deleted file mode 100644 index 30dc2a4a07..0000000000 --- a/assets/js/sync/components/sync-button.js +++ /dev/null @@ -1,49 +0,0 @@ -import { Button } from '@wordpress/components'; -import { useContext, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { external, update } from '@wordpress/icons'; - -/** - * Internal dependencies. - */ -import { SyncContext } from '../context'; - -/** - * Sync button component. - * - * @param {object} props Component props. - * @param {boolean} props.isDelete Whether button is a delete button. - * @param {Function} props.onClick Click callback. - * @returns {WPElement} Component. - */ -export default ({ isDelete, onClick }) => { - const { isSyncing } = useContext(SyncContext); - - /** - * Render. - */ - return ( - <> - - {!isDelete && ( - - )} - - ); -}; diff --git a/assets/js/sync/components/sync-controls.js b/assets/js/sync/components/sync-controls.js index b7f2ff4c06..b3c53949e2 100644 --- a/assets/js/sync/components/sync-controls.js +++ b/assets/js/sync/components/sync-controls.js @@ -2,13 +2,13 @@ * WordPress dependencies. */ import { Button } from '@wordpress/components'; -import { useContext, WPElement } from '@wordpress/element'; +import { WPElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; +import { update } from '@wordpress/icons'; /** * Internal dependencies. */ -import { SyncContext } from '../context'; import pause from './icons/pause'; import play from './icons/play'; import stop from './icons/stop'; @@ -17,33 +17,74 @@ import stop from './icons/stop'; * Sync button component. * * @param {object} props Component props. - * @param {Function} props.onPlayPause Play/pause button click callback. + * @param {boolean} props.disabled If controls are disabled. + * @param {boolean} props.isPaused If syncing is paused. + * @param {boolean} props.isSyncing If syncing is in progress. + * @param {Function} props.onPause Pause button click callback. + * @param {Function} props.onResume Play button click callback. * @param {Function} props.onStop Stop button click callback. + * @param {Function} props.onSync Sync button click callback. + * @param {boolean} props.showSync If sync button is shown. * @returns {WPElement} Component. */ -export default ({ onPlayPause, onStop }) => { - const { isPaused } = useContext(SyncContext); - +export default ({ disabled, isPaused, isSyncing, onPause, onResume, onStop, onSync, showSync }) => { /** * Render. */ return ( - <> - + + ) : null} + + {isSyncing ? ( + <> +
+ +
+ +
+ +
+ + ) : null} + + {showSync ? ( +
+ +
+ ) : null} + ); }; diff --git a/assets/js/sync/components/sync-log.js b/assets/js/sync/components/sync-log.js deleted file mode 100644 index 7ccbec18fb..0000000000 --- a/assets/js/sync/components/sync-log.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * WordPress dependencies. - */ -import { PanelBody } from '@wordpress/components'; -import { WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import Log from './common/log'; - -/** - * Sync button component. - * - * @param {object} props Component props. - * @param {object[]} props.messages Log messages. - * @returns {WPElement} Component. - */ -export default ({ messages }) => { - return ( - - - - ); -}; diff --git a/assets/js/sync/components/sync-logs.js b/assets/js/sync/components/sync-logs.js new file mode 100644 index 0000000000..de0a50a16b --- /dev/null +++ b/assets/js/sync/components/sync-logs.js @@ -0,0 +1,72 @@ +/** + * WordPress dependencies. + */ +import { TabPanel, ToggleControl } from '@wordpress/components'; +import { useState, WPElement } from '@wordpress/element'; +import { __, sprintf } from '@wordpress/i18n'; + +/** + * Internal dependencies. + */ +import MessageLog from './common/message-log'; + +/** + * Sync logs component. + * + * @param {object} props Component props. + * @param {object[]} props.messages Log messages. + * @returns {WPElement} Component. + */ +export default ({ messages }) => { + const [isOpen, setIsOpen] = useState(false); + + /** + * Messages with the error status. + */ + const errorMessages = messages.filter((m) => m.status === 'error'); + + /** + * Log tabs. + */ + const tabs = [ + { + messages, + name: 'full', + title: __('Full Log', 'elasticpress'), + }, + { + messages: errorMessages, + name: 'error', + title: sprintf( + /* translators: %d: Error message count. */ + __('Errors (%d)', 'elasticpress'), + errorMessages.length, + ), + }, + ]; + + /** + * Handle clicking show log button. + * + * @param {boolean} checked If toggle is checked. + * @returns {void} + */ + const onToggle = (checked) => { + setIsOpen(checked); + }; + + return ( + <> + + {isOpen ? ( + + {({ messages }) => } + + ) : null} + + ); +}; diff --git a/assets/js/sync/components/sync-progress.js b/assets/js/sync/components/sync-progress.js index c4c49cbdc6..0d3923f809 100644 --- a/assets/js/sync/components/sync-progress.js +++ b/assets/js/sync/components/sync-progress.js @@ -1,62 +1,83 @@ /** * WordPress dependencies. */ -import { useContext, useMemo, WPElement } from '@wordpress/element'; +import { Icon } from '@wordpress/components'; +import { useMemo, WPElement } from '@wordpress/element'; import { dateI18n } from '@wordpress/date'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies. */ -import { SyncContext } from '../context'; +import DateTime from './common/date-time'; import ProgressBar from './common/progress-bar'; +import sync from './icons/sync'; /** * Sync button component. * + * @param {object} props Component props. + * @param {boolean} props.isCli If progress is for a CLI sync. + * @param {boolean} props.isComplete If sync is complete. + * @param {boolean} props.isPaused If sync is paused. + * @param {number} props.itemsProcessed Number of items processed. + * @param {number} props.itemsTotal Total number of items. + * @param {string} props.dateTime Start date and time. * @returns {WPElement} Component. */ -export default () => { - const { isCli, isComplete, itemsProcessed, itemsTotal, syncStartDateTime } = - useContext(SyncContext); - +export default ({ isCli, isComplete, isPaused, itemsProcessed, itemsTotal, dateTime }) => { /** - * Sync progress message. + * Sync progress label. */ - const message = useMemo( + const label = useMemo( /** - * Determine appropriate sync status message. + * Determine appropriate sync status label. * - * @returns {string} Sync progress message. + * @returns {string} Sync progress label. */ () => { if (isComplete) { return __('Sync complete', 'elasticpress'); } + if (isPaused) { + return __('Sync paused', 'elasticpress'); + } + if (isCli) { return __('WP CLI sync in progress', 'elasticpress'); } - if (syncStartDateTime) { + if (dateTime) { return __('Sync in progress', 'elasticpress'); } return __('Starting sync', 'elasticpress'); }, - [isCli, isComplete, syncStartDateTime], + [isCli, isComplete, isPaused, dateTime], ); return ( -
- {message} - {syncStartDateTime ? ( - <> - {__('Start time:', 'elasticpress')}{' '} - {dateI18n('D, F d, Y H:i', syncStartDateTime)} - - ) : null} - +
+ + +
+ {label} + {dateTime ? ( + <> + {__('Started on', 'elasticpress')}{' '} + + + ) : null} +
+ +
+ +
); }; diff --git a/assets/js/sync/components/sync-status.js b/assets/js/sync/components/sync-status.js index 223545142e..5b944f0aae 100644 --- a/assets/js/sync/components/sync-status.js +++ b/assets/js/sync/components/sync-status.js @@ -1,32 +1,40 @@ /** * WordPress dependencies. */ -import { useContext, WPElement } from '@wordpress/element'; +import { Icon } from '@wordpress/components'; +import { dateI18n } from '@wordpress/date'; +import { WPElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies. */ -import { SyncContext } from '../context'; -import Status from './common/status'; +import DateTime from './common/date-time'; +import thumbsDown from './icons/thumbs-down'; +import thumbsUp from './icons/thumbs-up'; /** * Sync button component. * + * @param {object} props Component props. + * @param {string} props.dateTime Sync date and time. + * @param {boolean} props.isSuccess If sync was a success. * @returns {WPElement} Component. */ -export default () => { - const { lastSyncDateTime, lastSyncFailed } = useContext(SyncContext); - +export default ({ dateTime, isSuccess }) => { return ( - +

+ + + {isSuccess + ? __('Sync success on', 'elasticpress') + : __('Sync unsuccessful on', 'elasticpress')}{' '} + + +

); }; diff --git a/assets/js/sync/config.js b/assets/js/sync/config.js index 5b27301405..b667ebdfd0 100644 --- a/assets/js/sync/config.js +++ b/assets/js/sync/config.js @@ -2,21 +2,13 @@ * Window dependencies. */ const { - auto_start_index, - ajax_url, - index_meta = null, - is_epio, - ep_last_sync_date = null, - ep_last_sync_failed, + auto_start_index: autoIndex, + ajax_url: ajaxUrl, + index_meta: indexMeta = null, + is_epio: isEpio, + ep_last_sync_date: lastSyncDateTime = null, + ep_last_sync_failed: lastSyncFailed = false, nonce, } = window.epDash; -export { - auto_start_index, - ajax_url, - index_meta, - is_epio, - ep_last_sync_date, - ep_last_sync_failed, - nonce, -}; +export { autoIndex, ajaxUrl, indexMeta, isEpio, lastSyncDateTime, lastSyncFailed, nonce }; diff --git a/assets/js/sync/context.js b/assets/js/sync/context.js deleted file mode 100644 index bc2712e5ba..0000000000 --- a/assets/js/sync/context.js +++ /dev/null @@ -1,22 +0,0 @@ -import { createContext } from '@wordpress/element'; - -import { ep_last_sync_date, ep_last_sync_failed } from './config'; - -/** - * Initial state. - */ -export const initialState = { - isComplete: false, - isDeleting: false, - isSyncing: false, - itemsProcessed: 0, - itemsTotal: 100, - lastSyncDateTime: ep_last_sync_date, - lastSyncFailed: ep_last_sync_failed, - syncStartDateTime: null, -}; - -/** - * Context. - */ -export const SyncContext = createContext(initialState); diff --git a/assets/js/sync/hooks.js b/assets/js/sync/hooks.js index 52a321b830..e73aac8917 100644 --- a/assets/js/sync/hooks.js +++ b/assets/js/sync/hooks.js @@ -1,13 +1,13 @@ /** - * WordPress imports. + * WordPress dependencies. */ import apiFetch from '@wordpress/api-fetch'; import { useCallback, useRef } from '@wordpress/element'; /** - * Local imports. + * Internal dependencies. */ -import { ajax_url, nonce } from './config'; +import { ajaxUrl, nonce } from './config'; /** * Indexing hook. @@ -23,6 +23,33 @@ export const useIndex = () => { const abort = useRef(new AbortController()); const request = useRef(null); + const sendRequest = useCallback( + /** + * Send AJAX request. + * + * Silently catches abort errors and clears the current request on + * completion. + * + * @param {object} options Request options. + * @throws {Error} Any non-abort errors. + * @returns {Promise} Current request promise. + */ + (options) => { + request.current = apiFetch(options) + .catch((error) => { + if (error?.name !== 'AbortError' && !request.current) { + throw error; + } + }) + .finally(() => { + request.current = null; + }); + + return request.current; + }, + [], + ); + const cancelIndex = useCallback( /** * Send a request to cancel sync. @@ -33,27 +60,21 @@ export const useIndex = () => { abort.current.abort(); abort.current = new AbortController(); - const url = ajax_url; - const method = 'POST'; const body = new FormData(); - const { signal } = abort.current; body.append('action', 'ep_cancel_index'); body.append('nonce', nonce); - request.current = apiFetch({ url, method, body, signal }) - .catch((error) => { - if (error?.name !== 'AbortError' && !request.current) { - throw error; - } - }) - .finally(() => { - request.current = null; - }); + const options = { + url: ajaxUrl, + method: 'POST', + body, + signal: abort.current.signal, + }; - return request.current; + return sendRequest(options); }, - [], + [sendRequest], ); const index = useCallback( @@ -67,28 +88,22 @@ export const useIndex = () => { abort.current.abort(); abort.current = new AbortController(); - const url = ajax_url; - const method = 'POST'; const body = new FormData(); - const { signal } = abort.current; body.append('action', 'ep_index'); body.append('put_mapping', putMapping ? 1 : 0); body.append('nonce', nonce); - request.current = apiFetch({ url, method, body, signal }) - .catch((error) => { - if (error?.name !== 'AbortError' && !request.current) { - throw error; - } - }) - .finally(() => { - request.current = null; - }); + const options = { + url: ajaxUrl, + method: 'POST', + body, + signal: abort.current.signal, + }; - return request.current; + return sendRequest(options); }, - [], + [sendRequest], ); const indexStatus = useCallback( @@ -101,27 +116,21 @@ export const useIndex = () => { abort.current.abort(); abort.current = new AbortController(); - const url = ajax_url; - const method = 'POST'; const body = new FormData(); - const { signal } = abort.current; body.append('action', 'ep_cli_index'); body.append('nonce', nonce); - request.current = apiFetch({ url, method, body, signal }) - .catch((error) => { - if (error?.name !== 'AbortError' && !request.current) { - throw error; - } - }) - .finally(() => { - request.current = null; - }); + const options = { + url: ajaxUrl, + method: 'POST', + body, + signal: abort.current.signal, + }; - return request.current; + return sendRequest(options); }, - [], + [sendRequest], ); return { cancelIndex, index, indexStatus }; diff --git a/assets/js/sync/index.js b/assets/js/sync/index.js index f6b3acb4ab..f289bd8f33 100644 --- a/assets/js/sync/index.js +++ b/assets/js/sync/index.js @@ -1,22 +1,30 @@ +/** + * External dependencies. + */ +import { v4 as uuid } from 'uuid'; + /** * WordPress dependencies. */ -import { Panel, PanelBody } from '@wordpress/components'; +import { Button, Panel, PanelBody } from '@wordpress/components'; import { render, useCallback, useEffect, useRef, useState, WPElement } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; /** * Internal dependencies. */ -import { auto_start_index, is_epio, index_meta } from './config'; -import { initialState, SyncContext } from './context'; -import SyncButton from './components/sync-button'; +import { autoIndex, lastSyncDateTime, lastSyncFailed, isEpio, indexMeta } from './config'; +import { useIndex } from './hooks'; +import { + clearSyncParam, + getItemsProcessedFromIndexMeta, + getItemsTotalFromIndexMeta, +} from './utilities'; + import SyncControls from './components/sync-controls'; -import SyncLog from './components/sync-log'; +import SyncLog from './components/sync-logs'; import SyncProgress from './components/sync-progress'; import SyncStatus from './components/sync-status'; -import { useIndex } from './hooks'; -import { getItemsProcessedFromIndexMeta, getItemsTotalFromIndexMeta } from './utilities'; /** * App component. @@ -24,10 +32,33 @@ import { getItemsProcessedFromIndexMeta, getItemsTotalFromIndexMeta } from './ut * @returns {WPElement} App component. */ const App = () => { - const [log, setLog] = useState([]); - const [state, setState] = useState(initialState); + /** + * Indexing methods. + */ const { cancelIndex, index, indexStatus } = useIndex(); + /** + * Message log state. + */ + const [log, setLog] = useState([]); + + /** + * Sync state. + */ + const [state, setState] = useState({ + isComplete: false, + isDeleting: false, + isSyncing: false, + itemsProcessed: 0, + itemsTotal: 100, + lastSyncDateTime, + lastSyncFailed, + syncStartDateTime: null, + }); + + /** + * Current state reference. + */ const stateRef = useRef(state); /** @@ -41,9 +72,6 @@ const App = () => { setState((state) => ({ ...state, ...newState })); }; - /** - * Log message callback. - */ const logMessage = useCallback( /** * Log a message. @@ -58,18 +86,16 @@ const App = () => { const messages = Array.isArray(message) ? message : [message]; for (const message of messages) { - setLog((log) => [...log, { message, status, isDeleting }]); + setLog((log) => [...log, { message, status, isDeleting, id: uuid() }]); } }, [], ); - /** - * Complete sync callback. - */ - const completeSync = useCallback( + const syncCompleted = useCallback( /** - * Update sync status from totals. + * Set sync state to completed, with success based on the number of + * failures in the index totals. * * @param {object} indexTotals Index totals. * @returns {void} @@ -86,12 +112,12 @@ const App = () => { [], ); - /** - * Interrupt sync callback. - */ - const interruptSync = useCallback( + const syncInterrupted = useCallback( /** - * Interrupt a sync. + * Set sync state to interrupted. + * + * Logs an appropriate message based on the sync method and + * Elasticsearch hosting. * * @returns {void} */ @@ -105,7 +131,7 @@ const App = () => { 'Your indexing process has been stopped by WP-CLI and your %s index could be missing content. To restart indexing, please click the Start button or use WP-CLI commands to perform the reindex. Please note that search results could be incorrect or incomplete until the reindex finishes.', 'elasticpress', ), - is_epio + isEpio ? __('ElasticPress.io', 'elasticpress') : __('Elasticsearch', 'elasticpress'), ) @@ -117,15 +143,9 @@ const App = () => { [logMessage], ); - /** - * Update state from index meta callback. - */ - const updateStateFromIndexMeta = useCallback( + const syncInProgress = useCallback( /** - * Update sync status from index meta. - * - * Index meta is either present on the window object, if a sync is - * already in progress, or returned in response to index requests. + * Set state for a sync in progress from its index meta. * * @param {object} indexMeta Index meta. * @returns {void} @@ -133,7 +153,9 @@ const App = () => { (indexMeta) => { updateState({ isCli: indexMeta.method === 'cli', + isComplete: false, isDeleting: indexMeta.put_mapping, + isSyncing: true, itemsProcessed: getItemsProcessedFromIndexMeta(indexMeta), itemsTotal: getItemsTotalFromIndexMeta(indexMeta), syncStartDateTime: indexMeta.start_date_time, @@ -142,10 +164,7 @@ const App = () => { [], ); - /** - * Update sync status callback. - */ - const handleSyncResponse = useCallback( + const updateSyncState = useCallback( /** * Handle the response to a request to index. * @@ -162,7 +181,7 @@ const App = () => { return new Promise((resolve) => { /** - * Don't resolve if syncing has been stopped. + * Don't continue if syncing has been stopped. */ if (!isSyncing) { return; @@ -176,46 +195,46 @@ const App = () => { } /** - * If totals are present the index is complete. + * If totals are available the index is complete. */ if (!Array.isArray(totals)) { - completeSync(totals); + syncCompleted(totals); return; } /** - * Update sync state from index meta. + * Update sync progress from index meta. */ - updateStateFromIndexMeta(indexMeta); + syncInProgress(indexMeta); /** - * Handle sync interruption. + * Don't continue if the sync was interrupted externally. */ if (indexMeta.should_interrupt_sync) { - interruptSync(); + syncInterrupted(); return; } /** - * Don't resolve if syncing has been paused. + * Don't continue if syncing has been paused. */ if (isPaused) { - logMessage(__('Sync paused.', 'elasticpress'), 'info'); + logMessage(__('Sync paused', 'elasticpress'), 'info'); return; } + /** + * Syncing should continue. + */ resolve(indexMeta.method); }); }, - [completeSync, interruptSync, logMessage, updateStateFromIndexMeta], + [syncCompleted, syncInProgress, syncInterrupted, logMessage], ); - /** - * Get sync status callback. - */ - const syncStatus = useCallback( + const doIndexStatus = useCallback( /** - * Get the status of a sync. + * Check the status of a sync. * * Used to get the status of an external sync already in progress, such * as a WP CLI index. @@ -223,16 +242,12 @@ const App = () => { * @returns {void} */ () => { - updateState({ isComplete: false, isPaused: false, isSyncing: true }); - indexStatus().then(handleSyncResponse).then(syncStatus); + indexStatus().then(updateSyncState).then(doIndexStatus); }, - [handleSyncResponse, indexStatus], + [indexStatus, updateSyncState], ); - /** - * Sync callback. - */ - const sync = useCallback( + const doIndex = useCallback( /** * Start or continues a sync. * @@ -240,28 +255,78 @@ const App = () => { * @returns {void} */ (isDeleting) => { - updateState({ - isCli: false, - isComplete: false, - isDeleting, - isSyncing: true, - }); - index(isDeleting) - .then(handleSyncResponse) - .then((method) => { + .then(updateSyncState) + .then( /** * If an existing sync has been found just check its status, * otherwise continue syncing. + * + * @param {string} method Sync method. */ - if (method === 'cli') { - syncStatus(); - } else { - sync(isDeleting); - } - }); + (method) => { + if (method === 'cli') { + doIndexStatus(); + } else { + doIndex(isDeleting); + } + }, + ); }, - [handleSyncResponse, index, syncStatus], + [doIndexStatus, index, updateSyncState], + ); + + const pauseSync = useCallback( + /** + * Stop syncing. + * + * @returns {void} + */ + () => { + updateState({ isComplete: false, isPaused: true, isSyncing: true }); + }, + [], + ); + + const stopSync = useCallback( + /** + * Stop syncing. + * + * @returns {void} + */ + () => { + updateState({ isComplete: false, isPaused: false, isSyncing: false }); + cancelIndex(); + }, + [cancelIndex], + ); + + const resumeSync = useCallback( + /** + * Resume syncing. + * + * @returns {void} + */ + () => { + updateState({ isComplete: false, isPaused: false, isSyncing: true }); + doIndex(stateRef.current.isDeleting); + }, + [doIndex], + ); + + const startSync = useCallback( + /** + * Stop syncing. + * + * @param {boolean} isDeleting Whether to delete and sync. + * @returns {void} + */ + (isDeleting) => { + updateState({ isComplete: false, isPaused: false, isSyncing: true }); + updateState({ itemsProcessed: 0, itemsTotal: 100, syncStartDateTime: Date.now() }); + doIndex(isDeleting); + }, + [doIndex], ); /** @@ -270,85 +335,87 @@ const App = () => { * @returns {void} */ const onDelete = async () => { - updateState({ itemsProcessed: 0, itemsTotal: 100, syncStartDateTime: null }); - sync(true); + startSync(true); logMessage(__('Starting delete and sync…', 'elasticpress'), 'info'); }; /** - * Handle clicking play/pause button. + * Handle clicking pause button. + * + * @returns {void} */ - const onPlayPause = () => { - const { isDeleting, isPaused } = stateRef.current; - - if (isPaused) { - updateState({ isPaused: false }); - sync(isDeleting); - logMessage(__('Resuming sync…', 'elasticpress'), 'info'); - } else { - updateState({ isPaused: true }); - logMessage(__('Pausing sync…', 'elasticpress'), 'info'); - } + const onPause = () => { + pauseSync(); + logMessage(__('Pausing sync…', 'elasticpress'), 'info'); }; /** - * Handle clicking stop button. + * Handle clicking play button. * * @returns {void} */ - const onStop = () => { - updateState({ isSyncing: false }); - cancelIndex(); - logMessage(__('Sync stopped.', 'elasticpress'), 'info'); + const onResume = () => { + resumeSync(); + logMessage(__('Resuming sync…', 'elasticpress'), 'info'); }; /** - * Handle clicking sync button. + * Handle clicking stop button. * * @returns {void} */ - const onSync = async () => { - updateState({ itemsProcessed: 0, itemsTotal: 100, syncStartDateTime: null }); - sync(false); - logMessage(__('Starting sync…', 'elasticpress'), 'info'); + const onStop = () => { + stopSync(); + logMessage(__('Sync stopped', 'elasticpress'), 'info'); }; /** - * Clear sync parameter from the URL to prevent a refresh triggering a new - * sync. + * Handle clicking sync button. * * @returns {void} */ - const clearSearchParam = () => { - window.history.replaceState( - {}, - document.title, - document.location.pathname + document.location.search.replace(/&do_sync/, ''), - ); + const onSync = async () => { + startSync(false); + logMessage(__('Starting sync…', 'elasticpress'), 'info'); }; /** - * Set initial state. + * Initialize. * * @returns {void} */ - const handleInit = () => { - clearSearchParam(); - - if (index_meta) { - updateStateFromIndexMeta(index_meta); + const init = () => { + /** + * Clear sync parameter from the URL to prevent a refresh triggering a new + * sync. + */ + clearSyncParam(); - if (index_meta.method === 'cli') { - syncStatus(); + /** + * If a sync is in progress, update state to reflect its progress. + */ + if (indexMeta) { + syncInProgress(indexMeta); + pauseSync(); + + /** + * If the sync is a CLI sync, start getting its status. + */ + if (indexMeta.method === 'cli') { + doIndexStatus(); + logMessage(__('WP CLI sync in progress', 'elasticpress'), 'info'); } else { - updateState({ isComplete: false, isPaused: true, isSyncing: true }); + logMessage(__('Sync paused', 'elasticpress'), 'info'); } return; } - if (auto_start_index) { - sync(true); + /** + * Start an initial index. + */ + if (autoIndex) { + doIndex(true); logMessage(__('Starting delete and sync…', 'elasticpress'), 'info'); } }; @@ -356,79 +423,133 @@ const App = () => { /** * Effects. */ - useEffect(handleInit, [logMessage, sync, syncStatus, updateStateFromIndexMeta]); + useEffect(init, [doIndex, doIndexStatus, syncInProgress, logMessage, pauseSync]); /** * Render. */ return ( - -

{__('Sync Settings', 'elasticpress')}

- - -

- {__( - 'If you are missing data in your search results or have recently added custom content types to your site, you should run a sync to reflect these changes.', - 'elasticpress', - )} -

- - {state.lastSyncDateTime ? ( - <> -

{__('Last Sync', 'elasticpress')}

- - - ) : null} - - {state.isSyncing && !state.isDeleting && !state.isCli ? ( - - ) : ( - - )} +
+

{__('Sync Settings', 'elasticpress')}

+ + + +
+

+ {__( + 'If you are missing data in your search results or have recently added custom content types to your site, you should run a sync to reflect these changes.', + 'elasticpress', + )} +

+ + {state.lastSyncDateTime ? ( + <> +

+ {__('Last Sync', 'elasticpress')} +

+ + + ) : null} +
+ +
+ +
{!state.isDeleting && (state.isSyncing || state.isComplete) ? ( - +
+ +
) : null} + +
+ !m.isDeleting)} /> +
- {log.some((m) => !m.isDeleting) && ( - !m.isDeleting)} /> - )}
-

{__('Delete All Data and Sync', 'elasticpress')}

- - -

- {__( - 'If you are still having issues with your search results, you may need to do a completely fresh sync.', - 'elasticpress', - )} -

- - - - {state.isDeleting ? ( - <> - {state.isSyncing && !state.isCli ? ( - - ) : null} - - {state.isSyncing || state.isComplete ? : null} - +

{__('Delete All Data and Sync', 'elasticpress')}

+ + + +
+

+ {__( + 'If you are still having issues with your search results, you may need to do a completely fresh sync.', + 'elasticpress', + )} +

+ +

+ +

+
+ +
+ +
+ + {state.isDeleting && (state.isSyncing || state.isComplete) ? ( +
+ +
) : null} -

- {__( - 'All indexed data on ElasticPress will be deleted without affecting anything on your WordPress website. This may take a few hours depending on the amount of content that needs to be synced and indexed. While this is happenening, searches will use the default WordPress results', - 'elasticpress', - )} -

+
+ m.isDeleting)} /> +
+ +
+

+ {__( + 'All indexed data on ElasticPress will be deleted without affecting anything on your WordPress website. This may take a few hours depending on the amount of content that needs to be synced and indexed. While this is happenening, searches will use the default WordPress results', + 'elasticpress', + )} +

+
- {log.some((m) => m.isDeleting) && ( - m.isDeleting)} /> - )}
- +
); }; diff --git a/assets/js/sync/utilities.js b/assets/js/sync/utilities.js index bbf7075881..decee56ff5 100644 --- a/assets/js/sync/utilities.js +++ b/assets/js/sync/utilities.js @@ -1,3 +1,16 @@ +/** + * Clear sync parameter from the URL. + * + * @returns {void} + */ +export const clearSyncParam = () => { + window.history.replaceState( + {}, + document.title, + document.location.pathname + document.location.search.replace(/&do_sync/, ''), + ); +}; + /** * Get the total number of items from index meta. * diff --git a/includes/classes/Screen/Sync.php b/includes/classes/Screen/Sync.php index ff0d384d4e..fa677631ca 100644 --- a/includes/classes/Screen/Sync.php +++ b/includes/classes/Screen/Sync.php @@ -144,6 +144,7 @@ public function admin_enqueue_scripts() { ); wp_enqueue_style( 'wp-components' ); + wp_enqueue_style( 'wp-edit-post' ); wp_enqueue_style( 'ep_sync_style', diff --git a/package-lock.json b/package-lock.json index 0392342d1d..735b65c5f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "devDependencies": { "@wordpress/env": "^4.2.2", "10up-toolkit": "^3.0.0", + "classnames": "^2.3.1", "cypress": "^9.5.0", "cypress-file-upload": "^5.0.8", "eslint-plugin-cypress": "^2.12.1", @@ -6321,7 +6322,8 @@ }, "node_modules/classnames": { "version": "2.3.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" }, "node_modules/clean-css": { "version": "5.2.4", @@ -24179,7 +24181,9 @@ "dev": true }, "classnames": { - "version": "2.3.1" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" }, "clean-css": { "version": "5.2.4", diff --git a/package.json b/package.json index 4e8f213675..6382e06c40 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "devDependencies": { "@wordpress/env": "^4.2.2", "10up-toolkit": "^3.0.0", + "classnames": "^2.3.1", "cypress": "^9.5.0", "cypress-file-upload": "^5.0.8", "eslint-plugin-cypress": "^2.12.1", From 777f1e3c9a8b7cabb188d077da78236fa4b3b8ca Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 13:11:25 +1000 Subject: [PATCH 03/25] Remove unused class. --- assets/js/sync/components/sync-status.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/sync/components/sync-status.js b/assets/js/sync/components/sync-status.js index 5b944f0aae..22005f3b40 100644 --- a/assets/js/sync/components/sync-status.js +++ b/assets/js/sync/components/sync-status.js @@ -28,7 +28,7 @@ export default ({ dateTime, isSuccess }) => { isSuccess ? `ep-sync-status--success` : `ep-sync-status--error` }`} > - + {isSuccess ? __('Sync success on', 'elasticpress') From 55a670ea49d6277735e87182fb80b3ca84e4d4fa Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 13:11:43 +1000 Subject: [PATCH 04/25] Handle request errors. --- assets/js/sync/hooks.js | 12 +++------- assets/js/sync/index.js | 38 ++++++++++++++++++++++++++++---- includes/classes/Screen/Sync.php | 6 ++--- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/assets/js/sync/hooks.js b/assets/js/sync/hooks.js index e73aac8917..dc603a504a 100644 --- a/assets/js/sync/hooks.js +++ b/assets/js/sync/hooks.js @@ -35,15 +35,9 @@ export const useIndex = () => { * @returns {Promise} Current request promise. */ (options) => { - request.current = apiFetch(options) - .catch((error) => { - if (error?.name !== 'AbortError' && !request.current) { - throw error; - } - }) - .finally(() => { - request.current = null; - }); + request.current = apiFetch(options).finally(() => { + request.current = null; + }); return request.current; }, diff --git a/assets/js/sync/index.js b/assets/js/sync/index.js index f289bd8f33..f590f5244a 100644 --- a/assets/js/sync/index.js +++ b/assets/js/sync/index.js @@ -112,6 +112,35 @@ const App = () => { [], ); + const syncError = useCallback( + /** + * Handle an error in the sync request. + * + * @param {Error} error Request error. + * @returns {void} + */ + (error) => { + /** + * Any running requests are cancelled when a new request us made. + * We can handle this silently. + */ + if (error.name === 'AbortError') { + return; + } + + /** + * Log any messages. + */ + if (error.message) { + logMessage(error.message, 'error'); + } + + logMessage(__('Sync failed', 'elasticpress'), 'error'); + updateState({ isSyncing: false }); + }, + [logMessage], + ); + const syncInterrupted = useCallback( /** * Set sync state to interrupted. @@ -242,9 +271,9 @@ const App = () => { * @returns {void} */ () => { - indexStatus().then(updateSyncState).then(doIndexStatus); + indexStatus().then(updateSyncState).then(doIndexStatus).catch(syncError); }, - [indexStatus, updateSyncState], + [indexStatus, syncError, updateSyncState], ); const doIndex = useCallback( @@ -271,9 +300,10 @@ const App = () => { doIndex(isDeleting); } }, - ); + ) + .catch(syncError); }, - [doIndexStatus, index, updateSyncState], + [doIndexStatus, index, syncError, updateSyncState], ); const pauseSync = useCallback( diff --git a/includes/classes/Screen/Sync.php b/includes/classes/Screen/Sync.php index fa677631ca..863c2deddc 100644 --- a/includes/classes/Screen/Sync.php +++ b/includes/classes/Screen/Sync.php @@ -43,7 +43,7 @@ public function setup() { */ public function action_wp_ajax_ep_cli_index() { if ( ! check_ajax_referer( 'ep_dashboard_nonce', 'nonce', false ) || ! EP_DASHBOARD_SYNC ) { - wp_send_json_error(); + wp_send_json_error(null, 403); exit; } @@ -79,7 +79,7 @@ public function action_wp_ajax_ep_cli_index() { */ public function action_wp_ajax_ep_index() { if ( ! check_ajax_referer( 'ep_dashboard_nonce', 'nonce', false ) || ! EP_DASHBOARD_SYNC ) { - wp_send_json_error(); + wp_send_json_error(null, 403); exit; } @@ -107,7 +107,7 @@ public function action_wp_ajax_ep_index() { */ public function action_wp_ajax_ep_cancel_index() { if ( ! check_ajax_referer( 'ep_dashboard_nonce', 'nonce', false ) || ! EP_DASHBOARD_SYNC ) { - wp_send_json_error(); + wp_send_json_error(null, 403); exit; } From a2e34c2b2c3847de08271f89e80eb5a5a3d0ba21 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 15:28:32 +1000 Subject: [PATCH 05/25] Fix totals counting. --- assets/js/sync/utilities.js | 16 +++++++++++++--- includes/classes/IndexHelper.php | 6 ++---- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/assets/js/sync/utilities.js b/assets/js/sync/utilities.js index decee56ff5..57be5b6c9e 100644 --- a/assets/js/sync/utilities.js +++ b/assets/js/sync/utilities.js @@ -18,7 +18,7 @@ export const clearSyncParam = () => { * @returns {number} Number of items. */ export const getItemsTotalFromIndexMeta = (indexMeta) => { - let itemsTotal = indexMeta.totals.total; + let itemsTotal = 0; if (indexMeta.current_sync_item) { itemsTotal += indexMeta.current_sync_item.found_items; @@ -29,6 +29,10 @@ export const getItemsTotalFromIndexMeta = (indexMeta) => { itemsTotal, ); + itemsTotal += indexMeta.totals.failed; + itemsTotal += indexMeta.totals.skipped; + itemsTotal += indexMeta.totals.synced; + return itemsTotal; }; @@ -39,11 +43,17 @@ export const getItemsTotalFromIndexMeta = (indexMeta) => { * @returns {number} Number of processed items. */ export const getItemsProcessedFromIndexMeta = (indexMeta) => { - let itemsProcessed = indexMeta.totals.total; + let itemsProcessed = 0; if (indexMeta.current_sync_item) { - itemsProcessed += indexMeta.current_sync_item.total; + itemsProcessed += indexMeta.current_sync_item.failed; + itemsProcessed += indexMeta.current_sync_item.skipped; + itemsProcessed += indexMeta.current_sync_item.synced; } + itemsProcessed += indexMeta.totals.failed; + itemsProcessed += indexMeta.totals.skipped; + itemsProcessed += indexMeta.totals.synced; + return itemsProcessed; }; diff --git a/includes/classes/IndexHelper.php b/includes/classes/IndexHelper.php index 427b14a21e..d271883079 100644 --- a/includes/classes/IndexHelper.php +++ b/includes/classes/IndexHelper.php @@ -729,6 +729,8 @@ protected function index_cleanup() { $current_sync_item = $this->index_meta['current_sync_item']; + $this->index_meta['offset'] = 0; + $this->index_meta['current_sync_item'] = null; $this->index_meta['totals']['total'] += $current_sync_item['total']; $this->index_meta['totals']['synced'] += $current_sync_item['synced']; $this->index_meta['totals']['skipped'] += $current_sync_item['skipped']; @@ -739,8 +741,6 @@ protected function index_cleanup() { ); if ( $current_sync_item['failed'] ) { - $this->index_meta['current_sync_item']['failed'] = 0; - if ( ! empty( $current_sync_item['blog_id'] ) && defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { $message = sprintf( /* translators: 1: indexable (plural), 2: Blog ID, 3: number of failed objects */ @@ -761,8 +761,6 @@ protected function index_cleanup() { $this->output( $message, 'warning' ); } - $this->index_meta['offset'] = 0; - $this->index_meta['current_sync_item'] = null; if ( ! empty( $current_sync_item['blog_id'] ) && defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { $message = sprintf( From f3db232e227f5c5f4d556ca4962f9f5f1485d281 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 15:29:23 +1000 Subject: [PATCH 06/25] Move sync page markup to its own component. --- .../js/sync/components/common/progress-bar.js | 3 +- assets/js/sync/components/sync-page.js | 179 ++++++++++++++++++ .../{sync-controls.js => sync/controls.js} | 6 +- .../components/{sync-logs.js => sync/log.js} | 4 +- .../{sync-progress.js => sync/progress.js} | 6 +- .../{sync-status.js => sync/status.js} | 6 +- assets/js/sync/index.js | 149 ++------------- 7 files changed, 207 insertions(+), 146 deletions(-) create mode 100644 assets/js/sync/components/sync-page.js rename assets/js/sync/components/{sync-controls.js => sync/controls.js} (95%) rename assets/js/sync/components/{sync-logs.js => sync/log.js} (94%) rename assets/js/sync/components/{sync-progress.js => sync/progress.js} (93%) rename assets/js/sync/components/{sync-status.js => sync/status.js} (87%) diff --git a/assets/js/sync/components/common/progress-bar.js b/assets/js/sync/components/common/progress-bar.js index f910580e5c..8bd4ce7d2c 100644 --- a/assets/js/sync/components/common/progress-bar.js +++ b/assets/js/sync/components/common/progress-bar.js @@ -20,8 +20,7 @@ export default ({ isComplete, current, total }) => { aria-valuemax={100} aria-valuemin={0} aria-valuenow={now} - className={`ep-sync-progress-bar ${isComplete ? `ep-sync-progress-bar--complete` : ``} - `} + className={`ep-sync-progress-bar ${isComplete ? `ep-sync-progress-bar--complete` : ``}`} role="progressbar" >
{ + return ( +
+

{__('Sync Settings', 'elasticpress')}

+ + + +
+

+ {__( + 'If you are missing data in your search results or have recently added custom content types to your site, you should run a sync to reflect these changes.', + 'elasticpress', + )} +

+ + {lastSyncDateTime ? ( + <> +

+ {__('Last Sync', 'elasticpress')} +

+ + + ) : null} +
+ +
+ +
+ + {!isDeleting && (isSyncing || isComplete) ? ( +
+ +
+ ) : null} + +
+ !m.isDeleting)} /> +
+
+
+ +

{__('Delete All Data and Sync', 'elasticpress')}

+ + + +
+

+ {__( + 'If you are still having issues with your search results, you may need to do a completely fresh sync.', + 'elasticpress', + )} +

+ +

+ +

+
+ +
+ +
+ + {isDeleting && (isSyncing || isComplete) ? ( +
+ +
+ ) : null} + +
+ m.isDeleting)} /> +
+ +
+

+ {__( + 'All indexed data on ElasticPress will be deleted without affecting anything on your WordPress website. This may take a few hours depending on the amount of content that needs to be synced and indexed. While this is happenening, searches will use the default WordPress results', + 'elasticpress', + )} +

+
+
+
+
+ ); +}; diff --git a/assets/js/sync/components/sync-controls.js b/assets/js/sync/components/sync/controls.js similarity index 95% rename from assets/js/sync/components/sync-controls.js rename to assets/js/sync/components/sync/controls.js index b3c53949e2..6197521b86 100644 --- a/assets/js/sync/components/sync-controls.js +++ b/assets/js/sync/components/sync/controls.js @@ -9,9 +9,9 @@ import { update } from '@wordpress/icons'; /** * Internal dependencies. */ -import pause from './icons/pause'; -import play from './icons/play'; -import stop from './icons/stop'; +import pause from '../icons/pause'; +import play from '../icons/play'; +import stop from '../icons/stop'; /** * Sync button component. diff --git a/assets/js/sync/components/sync-logs.js b/assets/js/sync/components/sync/log.js similarity index 94% rename from assets/js/sync/components/sync-logs.js rename to assets/js/sync/components/sync/log.js index de0a50a16b..c9e1c2921a 100644 --- a/assets/js/sync/components/sync-logs.js +++ b/assets/js/sync/components/sync/log.js @@ -8,7 +8,7 @@ import { __, sprintf } from '@wordpress/i18n'; /** * Internal dependencies. */ -import MessageLog from './common/message-log'; +import MessageLog from '../common/message-log'; /** * Sync logs component. @@ -23,7 +23,7 @@ export default ({ messages }) => { /** * Messages with the error status. */ - const errorMessages = messages.filter((m) => m.status === 'error'); + const errorMessages = messages.filter((m) => m.status === 'error' || m.status === 'warning'); /** * Log tabs. diff --git a/assets/js/sync/components/sync-progress.js b/assets/js/sync/components/sync/progress.js similarity index 93% rename from assets/js/sync/components/sync-progress.js rename to assets/js/sync/components/sync/progress.js index 0d3923f809..cfdcfbc477 100644 --- a/assets/js/sync/components/sync-progress.js +++ b/assets/js/sync/components/sync/progress.js @@ -9,9 +9,9 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies. */ -import DateTime from './common/date-time'; -import ProgressBar from './common/progress-bar'; -import sync from './icons/sync'; +import DateTime from '../common/date-time'; +import ProgressBar from '../common/progress-bar'; +import sync from '../icons/sync'; /** * Sync button component. diff --git a/assets/js/sync/components/sync-status.js b/assets/js/sync/components/sync/status.js similarity index 87% rename from assets/js/sync/components/sync-status.js rename to assets/js/sync/components/sync/status.js index 22005f3b40..5025b5d02d 100644 --- a/assets/js/sync/components/sync-status.js +++ b/assets/js/sync/components/sync/status.js @@ -9,9 +9,9 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies. */ -import DateTime from './common/date-time'; -import thumbsDown from './icons/thumbs-down'; -import thumbsUp from './icons/thumbs-up'; +import DateTime from '../common/date-time'; +import thumbsDown from '../icons/thumbs-down'; +import thumbsUp from '../icons/thumbs-up'; /** * Sync button component. diff --git a/assets/js/sync/index.js b/assets/js/sync/index.js index f590f5244a..26defadb84 100644 --- a/assets/js/sync/index.js +++ b/assets/js/sync/index.js @@ -6,7 +6,6 @@ import { v4 as uuid } from 'uuid'; /** * WordPress dependencies. */ -import { Button, Panel, PanelBody } from '@wordpress/components'; import { render, useCallback, useEffect, useRef, useState, WPElement } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; @@ -20,11 +19,7 @@ import { getItemsProcessedFromIndexMeta, getItemsTotalFromIndexMeta, } from './utilities'; - -import SyncControls from './components/sync-controls'; -import SyncLog from './components/sync-logs'; -import SyncProgress from './components/sync-progress'; -import SyncStatus from './components/sync-status'; +import SyncPage from './components/sync-page'; /** * App component. @@ -76,7 +71,7 @@ const App = () => { /** * Log a message. * - * @param {string} message Message/s to log. + * @param {array|string} message Message/s to log. * @param {string} status Message status. * @returns {void} */ @@ -352,8 +347,8 @@ const App = () => { * @returns {void} */ (isDeleting) => { - updateState({ isComplete: false, isPaused: false, isSyncing: true }); - updateState({ itemsProcessed: 0, itemsTotal: 100, syncStartDateTime: Date.now() }); + updateState({ isComplete: false, isDeleting, isPaused: false, isSyncing: true }); + updateState({ itemsProcessed: 0, syncStartDateTime: Date.now() }); doIndex(isDeleting); }, [doIndex], @@ -426,7 +421,6 @@ const App = () => { */ if (indexMeta) { syncInProgress(indexMeta); - pauseSync(); /** * If the sync is a CLI sync, start getting its status. @@ -435,6 +429,7 @@ const App = () => { doIndexStatus(); logMessage(__('WP CLI sync in progress', 'elasticpress'), 'info'); } else { + pauseSync(); logMessage(__('Sync paused', 'elasticpress'), 'info'); } @@ -445,7 +440,7 @@ const App = () => { * Start an initial index. */ if (autoIndex) { - doIndex(true); + startSync(true); logMessage(__('Starting delete and sync…', 'elasticpress'), 'info'); } }; @@ -453,133 +448,21 @@ const App = () => { /** * Effects. */ - useEffect(init, [doIndex, doIndexStatus, syncInProgress, logMessage, pauseSync]); + useEffect(init, [doIndexStatus, syncInProgress, logMessage, pauseSync, startSync]); /** * Render. */ return ( -
-

{__('Sync Settings', 'elasticpress')}

- - - -
-

- {__( - 'If you are missing data in your search results or have recently added custom content types to your site, you should run a sync to reflect these changes.', - 'elasticpress', - )} -

- - {state.lastSyncDateTime ? ( - <> -

- {__('Last Sync', 'elasticpress')} -

- - - ) : null} -
- -
- -
- - {!state.isDeleting && (state.isSyncing || state.isComplete) ? ( -
- -
- ) : null} - -
- !m.isDeleting)} /> -
-
-
- -

{__('Delete All Data and Sync', 'elasticpress')}

- - - -
-

- {__( - 'If you are still having issues with your search results, you may need to do a completely fresh sync.', - 'elasticpress', - )} -

- -

- -

-
- -
- -
- - {state.isDeleting && (state.isSyncing || state.isComplete) ? ( -
- -
- ) : null} - -
- m.isDeleting)} /> -
- -
-

- {__( - 'All indexed data on ElasticPress will be deleted without affecting anything on your WordPress website. This may take a few hours depending on the amount of content that needs to be synced and indexed. While this is happenening, searches will use the default WordPress results', - 'elasticpress', - )} -

-
-
-
-
+ ); }; From d9e9c2c64280cebea9592de4ea4b8b7d1997209c Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 15:29:44 +1000 Subject: [PATCH 07/25] Fix type capitalisation. --- assets/js/sync/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/sync/index.js b/assets/js/sync/index.js index 26defadb84..5abf32dff5 100644 --- a/assets/js/sync/index.js +++ b/assets/js/sync/index.js @@ -71,7 +71,7 @@ const App = () => { /** * Log a message. * - * @param {array|string} message Message/s to log. + * @param {Array|string} message Message/s to log. * @param {string} status Message status. * @returns {void} */ From 87daa80a67cdd8e1afc58abbe3082f1b9dacfbcf Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 15:31:38 +1000 Subject: [PATCH 08/25] Fix typo. --- assets/js/sync/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/sync/index.js b/assets/js/sync/index.js index 5abf32dff5..c510ea8920 100644 --- a/assets/js/sync/index.js +++ b/assets/js/sync/index.js @@ -116,7 +116,7 @@ const App = () => { */ (error) => { /** - * Any running requests are cancelled when a new request us made. + * Any running requests are cancelled when a new request is made. * We can handle this silently. */ if (error.name === 'AbortError') { From f9a0eb5417f5c0b1c365234490ee8298f3971edf Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 15:35:00 +1000 Subject: [PATCH 09/25] Open learn more in new tab. --- assets/js/sync/components/sync/controls.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/sync/components/sync/controls.js b/assets/js/sync/components/sync/controls.js index 6197521b86..a7b61bfd85 100644 --- a/assets/js/sync/components/sync/controls.js +++ b/assets/js/sync/components/sync/controls.js @@ -79,6 +79,7 @@ export default ({ disabled, isPaused, isSyncing, onPause, onResume, onStop, onSy
+ ); }; From c5f989549eb6a5934614c70894a7eabc6fcba040 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 16:03:18 +1000 Subject: [PATCH 12/25] Add warning icon. --- assets/css/sync.css | 2 ++ assets/css/sync/status.css | 5 +++-- assets/css/sync/warning.css | 10 ++++++++++ assets/js/sync/components/sync-page.js | 6 ++++-- 4 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 assets/css/sync/warning.css diff --git a/assets/css/sync.css b/assets/css/sync.css index d259f74af5..5d3884fbb5 100644 --- a/assets/css/sync.css +++ b/assets/css/sync.css @@ -7,11 +7,13 @@ @import "sync/progress-bar.css"; @import "sync/status.css"; @import "sync/time.css"; +@import "sync/warning.css"; :root { --ep-sync-color-black: #1a1e24; --ep-sync-color-error: #b52727; --ep-sync-color-light-grey: #f0f0f0; --ep-sync-color-success: #46b450; + --ep-sync-color-warning: #ffb359; --ep-sync-color-white: #fff; } diff --git a/assets/css/sync/status.css b/assets/css/sync/status.css index 481f452a64..7c3f14899b 100644 --- a/assets/css/sync/status.css +++ b/assets/css/sync/status.css @@ -1,10 +1,11 @@ .ep-sync-status { align-items: center; - display: flex; + display: grid; + grid-gap: 0.5rem; + grid-template-columns: min-content auto; & svg { fill: var(--ep-sync-color-error); - margin-right: 0.5em; } } diff --git a/assets/css/sync/warning.css b/assets/css/sync/warning.css new file mode 100644 index 0000000000..a96cbbe375 --- /dev/null +++ b/assets/css/sync/warning.css @@ -0,0 +1,10 @@ +.ep-sync-warning { + display: grid; + grid-gap: 0.5rem; + grid-template-columns: min-content auto; + + & svg { + fill: var(--ep-sync-color-warning); + margin-top: -3px; + } +} diff --git a/assets/js/sync/components/sync-page.js b/assets/js/sync/components/sync-page.js index 0327e9570d..ee34d8cd22 100644 --- a/assets/js/sync/components/sync-page.js +++ b/assets/js/sync/components/sync-page.js @@ -1,9 +1,10 @@ /** * WordPress dependencies. */ -import { Button, Panel, PanelBody } from '@wordpress/components'; +import { Button, Icon, Panel, PanelBody } from '@wordpress/components'; import { WPElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; +import { warning } from '@wordpress/icons'; /** * Internal dependencies. @@ -165,7 +166,8 @@ export default ({
-

+

+ {__( 'All indexed data on ElasticPress will be deleted without affecting anything on your WordPress website. This may take a few hours depending on the amount of content that needs to be synced and indexed. While this is happenening, searches will use the default WordPress results', 'elasticpress', From ba45e729b854f2b68b1ce07c2a4bd1b5e98ffb8a Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 16:18:59 +1000 Subject: [PATCH 13/25] Fix linting errors. --- includes/classes/Feature/Search/Search.php | 2 +- includes/classes/IndexHelper.php | 5 ++--- includes/classes/Indexable/User/User.php | 4 ++-- includes/classes/Screen/Sync.php | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/includes/classes/Feature/Search/Search.php b/includes/classes/Feature/Search/Search.php index 12250e9402..b2b22add31 100644 --- a/includes/classes/Feature/Search/Search.php +++ b/includes/classes/Feature/Search/Search.php @@ -59,7 +59,7 @@ public function __construct() { $this->requires_install_reindex = false; - $this->default_settings = [ + $this->default_settings = [ 'decaying_enabled' => '1', 'synonyms_editor_mode' => 'simple', 'highlight_enabled' => '0', diff --git a/includes/classes/IndexHelper.php b/includes/classes/IndexHelper.php index d271883079..50b1b5f5e9 100644 --- a/includes/classes/IndexHelper.php +++ b/includes/classes/IndexHelper.php @@ -729,8 +729,8 @@ protected function index_cleanup() { $current_sync_item = $this->index_meta['current_sync_item']; - $this->index_meta['offset'] = 0; - $this->index_meta['current_sync_item'] = null; + $this->index_meta['offset'] = 0; + $this->index_meta['current_sync_item'] = null; $this->index_meta['totals']['total'] += $current_sync_item['total']; $this->index_meta['totals']['synced'] += $current_sync_item['synced']; $this->index_meta['totals']['skipped'] += $current_sync_item['skipped']; @@ -761,7 +761,6 @@ protected function index_cleanup() { $this->output( $message, 'warning' ); } - if ( ! empty( $current_sync_item['blog_id'] ) && defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { $message = sprintf( /* translators: 1: indexable (plural), 2: Blog ID, 3: number of synced objects */ diff --git a/includes/classes/Indexable/User/User.php b/includes/classes/Indexable/User/User.php index 40136f9367..ada5fe2d5c 100644 --- a/includes/classes/Indexable/User/User.php +++ b/includes/classes/Indexable/User/User.php @@ -134,7 +134,7 @@ public function format_args( $query_vars, $query ) { // If there are no specific roles named, make sure the user is a member of the site. if ( empty( $query_vars['role'] ) && empty( $query_vars['role__in'] ) && empty( $query_vars['role__not_in'] ) ) { - $filter['bool']['must'][] = array( + $filter['bool']['must'][] = array( 'exists' => array( 'field' => 'capabilities.' . $blog_id . '.roles', ), @@ -747,7 +747,7 @@ public function query_db( $args ) { * WP_User_Query doesn't let us get users across all blogs easily. This is the best * way to do that. */ - // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared $objects = $wpdb->get_results( $wpdb->prepare( "SELECT SQL_CALC_FOUND_ROWS ID FROM {$wpdb->users} {$orderby} LIMIT %d, %d", (int) $args['offset'], (int) $args['number'] ) ); return [ diff --git a/includes/classes/Screen/Sync.php b/includes/classes/Screen/Sync.php index 863c2deddc..ea654261e0 100644 --- a/includes/classes/Screen/Sync.php +++ b/includes/classes/Screen/Sync.php @@ -43,7 +43,7 @@ public function setup() { */ public function action_wp_ajax_ep_cli_index() { if ( ! check_ajax_referer( 'ep_dashboard_nonce', 'nonce', false ) || ! EP_DASHBOARD_SYNC ) { - wp_send_json_error(null, 403); + wp_send_json_error( null, 403 ); exit; } @@ -79,7 +79,7 @@ public function action_wp_ajax_ep_cli_index() { */ public function action_wp_ajax_ep_index() { if ( ! check_ajax_referer( 'ep_dashboard_nonce', 'nonce', false ) || ! EP_DASHBOARD_SYNC ) { - wp_send_json_error(null, 403); + wp_send_json_error( null, 403 ); exit; } @@ -107,7 +107,7 @@ public function action_wp_ajax_ep_index() { */ public function action_wp_ajax_ep_cancel_index() { if ( ! check_ajax_referer( 'ep_dashboard_nonce', 'nonce', false ) || ! EP_DASHBOARD_SYNC ) { - wp_send_json_error(null, 403); + wp_send_json_error( null, 403 ); exit; } From 423fbce88ad48b298328aba8d8255aaa6e0d17f4 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 17:12:05 +1000 Subject: [PATCH 14/25] Update Cypress tests. --- assets/css/sync/button.css | 5 +-- assets/js/sync/components/sync-page.js | 1 + assets/js/sync/components/sync/controls.js | 32 +++++++++++------ .../integration/dashboard-sync.spec.js | 34 ++++++++----------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/assets/css/sync/button.css b/assets/css/sync/button.css index 9870395de4..784c320788 100644 --- a/assets/css/sync/button.css +++ b/assets/css/sync/button.css @@ -1,9 +1,9 @@ .ep-sync-button { - height: 4rem; - width: 100%; &.components-button.has-icon.has-text { + height: 4rem; justify-content: center; + width: 100%; & svg { height: 2em; @@ -18,6 +18,7 @@ font-weight: 700; } +.ep-sync-button--pause, .ep-sync-button--resume, .ep-sync-button--stop { flex-direction: column; diff --git a/assets/js/sync/components/sync-page.js b/assets/js/sync/components/sync-page.js index ee34d8cd22..c1fadefbe6 100644 --- a/assets/js/sync/components/sync-page.js +++ b/assets/js/sync/components/sync-page.js @@ -127,6 +127,7 @@ export default ({

+

+ {isPaused ? ( + + ) : ( + + )}
diff --git a/tests/cypress/integration/dashboard-sync.spec.js b/tests/cypress/integration/dashboard-sync.spec.js index c28d7daba3..bbf1aeb33d 100644 --- a/tests/cypress/integration/dashboard-sync.spec.js +++ b/tests/cypress/integration/dashboard-sync.spec.js @@ -18,8 +18,8 @@ describe('Dashboard Sync', () => { } function resumeAndWait() { - cy.get('.ep-delete-data-and-sync .resume-sync').click(); - cy.get('.ep-delete-data-and-sync .ep-sync-box__progress-info', { + cy.get('.ep-sync-button--resume').click(); + cy.get('.ep-sync-progress', { timeout: Cypress.config('elasticPressIndexTimeout'), }).should('contain.text', 'Sync completed'); } @@ -37,8 +37,8 @@ describe('Dashboard Sync', () => { it('Can index content and see indexes names in the Health Screen', () => { cy.visitAdminPage('admin.php?page=elasticpress-sync'); - cy.get('.ep-delete-data-and-sync__button-delete').click(); - cy.get('.ep-delete-data-and-sync .ep-sync-box__progress-info', { + cy.get('.ep-sync-button--delete').click(); + cy.get('.ep-sync-progress', { timeout: Cypress.config('elasticPressIndexTimeout'), }).should('contain.text', 'Sync completed'); @@ -55,8 +55,8 @@ describe('Dashboard Sync', () => { ); cy.visitAdminPage('admin.php?page=elasticpress-sync'); - cy.get('.ep-delete-data-and-sync__button-delete').click(); - cy.get('.ep-delete-data-and-sync .ep-sync-box__progress-info', { + cy.get('.ep-sync-button--delete').click(); + cy.get('.ep-sync-progress', { timeout: Cypress.config('elasticPressIndexTimeout'), }).should('contain.text', 'Sync completed'); @@ -85,8 +85,8 @@ describe('Dashboard Sync', () => { ); cy.visitAdminPage('network/admin.php?page=elasticpress-sync'); - cy.get('.ep-delete-data-and-sync__button-delete').click(); - cy.get('.ep-delete-data-and-sync .ep-sync-box__progress-info', { + cy.get('.ep-sync-button--delete').click(); + cy.get('.ep-sync-progress', { timeout: Cypress.config('elasticPressIndexTimeout'), }).should('contain.text', 'Sync completed'); @@ -120,17 +120,14 @@ describe('Dashboard Sync', () => { cy.visitAdminPage('admin.php?page=elasticpress-sync'); cy.intercept('POST', '/wp-admin/admin-ajax.php*').as('ajaxRequest'); - cy.get('.ep-delete-data-and-sync__button-delete').click(); + cy.get('.ep-sync-button--delete').click(); cy.wait('@ajaxRequest').its('response.statusCode').should('eq', 200); - cy.get('.ep-delete-data-and-sync .pause-sync').should('be.visible'); + cy.get('.ep-sync-button--pause').should('be.visible'); cy.visitAdminPage('index.php'); cy.visitAdminPage('admin.php?page=elasticpress-sync'); - cy.get('.ep-delete-data-and-sync .ep-sync-box__progress-info').should( - 'contain.text', - 'Sync in progress', - ); + cy.get('.ep-sync-progress').should('contain.text', 'Sync in progress'); resumeAndWait(); @@ -144,7 +141,7 @@ describe('Dashboard Sync', () => { cy.visitAdminPage('admin.php?page=elasticpress-sync'); cy.intercept('POST', '/wp-admin/admin-ajax.php*').as('ajaxRequest'); - cy.get('.ep-delete-data-and-sync__button-delete').click(); + cy.get('.ep-sync-button--delete').click(); cy.wait('@ajaxRequest').its('response.statusCode').should('eq', 200); cy.visitAdminPage('admin.php?page=elasticpress'); @@ -164,12 +161,11 @@ describe('Dashboard Sync', () => { cy.visitAdminPage('admin.php?page=elasticpress-sync'); cy.intercept('POST', '/wp-admin/admin-ajax.php*').as('ajaxRequest'); - cy.get('.ep-delete-data-and-sync__button-delete').click(); + cy.get('.ep-sync-button--delete').click(); cy.wait('@ajaxRequest').its('response.statusCode').should('eq', 200); - cy.get('.ep-delete-data-and-sync .pause-sync').should('be.visible'); - cy.get('.ep-delete-data-and-sync .pause-sync').click(); - cy.wait('@ajaxRequest').its('response.statusCode').should('eq', 200); + cy.get('.ep-sync-button--pause').should('be.visible'); + cy.get('.ep-sync-button--pause').click(); cy.wpCli('wp elasticpress index', true) .its('stderr') From 37a96260f8c9b5deee5cb4ed0caea5346d839035 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 17:36:29 +1000 Subject: [PATCH 15/25] Fix complete message in test. --- assets/js/sync/components/sync/progress.js | 8 ++------ .../cypress/integration/dashboard-sync.spec.js | 18 +++++++++--------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/assets/js/sync/components/sync/progress.js b/assets/js/sync/components/sync/progress.js index cfdcfbc477..f6e98fe9cf 100644 --- a/assets/js/sync/components/sync/progress.js +++ b/assets/js/sync/components/sync/progress.js @@ -48,13 +48,9 @@ export default ({ isCli, isComplete, isPaused, itemsProcessed, itemsTotal, dateT return __('WP CLI sync in progress', 'elasticpress'); } - if (dateTime) { - return __('Sync in progress', 'elasticpress'); - } - - return __('Starting sync', 'elasticpress'); + return __('Sync in progress', 'elasticpress'); }, - [isCli, isComplete, isPaused, dateTime], + [isCli, isComplete, isPaused], ); return ( diff --git a/tests/cypress/integration/dashboard-sync.spec.js b/tests/cypress/integration/dashboard-sync.spec.js index bbf1aeb33d..b10cfa999a 100644 --- a/tests/cypress/integration/dashboard-sync.spec.js +++ b/tests/cypress/integration/dashboard-sync.spec.js @@ -19,9 +19,9 @@ describe('Dashboard Sync', () => { function resumeAndWait() { cy.get('.ep-sync-button--resume').click(); - cy.get('.ep-sync-progress', { + cy.get('.ep-sync-progress strong', { timeout: Cypress.config('elasticPressIndexTimeout'), - }).should('contain.text', 'Sync completed'); + }).should('contain.text', 'Sync complete'); } before(() => { @@ -38,9 +38,9 @@ describe('Dashboard Sync', () => { it('Can index content and see indexes names in the Health Screen', () => { cy.visitAdminPage('admin.php?page=elasticpress-sync'); cy.get('.ep-sync-button--delete').click(); - cy.get('.ep-sync-progress', { + cy.get('.ep-sync-progress strong', { timeout: Cypress.config('elasticPressIndexTimeout'), - }).should('contain.text', 'Sync completed'); + }).should('contain.text', 'Sync complete'); canSeeIndexesNames(); }); @@ -56,9 +56,9 @@ describe('Dashboard Sync', () => { cy.visitAdminPage('admin.php?page=elasticpress-sync'); cy.get('.ep-sync-button--delete').click(); - cy.get('.ep-sync-progress', { + cy.get('.ep-sync-progress strong', { timeout: Cypress.config('elasticPressIndexTimeout'), - }).should('contain.text', 'Sync completed'); + }).should('contain.text', 'Sync complete'); cy.visitAdminPage('admin.php?page=elasticpress-health'); cy.get('.wrap').should( @@ -86,9 +86,9 @@ describe('Dashboard Sync', () => { cy.visitAdminPage('network/admin.php?page=elasticpress-sync'); cy.get('.ep-sync-button--delete').click(); - cy.get('.ep-sync-progress', { + cy.get('.ep-sync-progress strong', { timeout: Cypress.config('elasticPressIndexTimeout'), - }).should('contain.text', 'Sync completed'); + }).should('contain.text', 'Sync complete'); cy.visitAdminPage('network/admin.php?page=elasticpress-health'); cy.get('.wrap').should( @@ -127,7 +127,7 @@ describe('Dashboard Sync', () => { cy.visitAdminPage('index.php'); cy.visitAdminPage('admin.php?page=elasticpress-sync'); - cy.get('.ep-sync-progress').should('contain.text', 'Sync in progress'); + cy.get('.ep-sync-progress strong').should('contain.text', 'Sync in progress'); resumeAndWait(); From 8d45859a862700fa04429b9aca231dd61560ac99 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 10 May 2022 17:47:33 +1000 Subject: [PATCH 16/25] Remove background from time in sync progress. --- assets/css/sync.css | 1 - assets/css/sync/status.css | 6 ++++++ assets/css/sync/time.css | 5 ----- assets/js/sync/components/common/date-time.js | 4 ++-- assets/js/sync/components/sync/status.js | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) delete mode 100644 assets/css/sync/time.css diff --git a/assets/css/sync.css b/assets/css/sync.css index 5d3884fbb5..3d513296a1 100644 --- a/assets/css/sync.css +++ b/assets/css/sync.css @@ -6,7 +6,6 @@ @import "sync/progress.css"; @import "sync/progress-bar.css"; @import "sync/status.css"; -@import "sync/time.css"; @import "sync/warning.css"; :root { diff --git a/assets/css/sync/status.css b/assets/css/sync/status.css index 7c3f14899b..178a1cc270 100644 --- a/assets/css/sync/status.css +++ b/assets/css/sync/status.css @@ -15,3 +15,9 @@ fill: var(--ep-sync-color-success); } } + +.ep-sync-status__time { + background-color: var(--ep-sync-color-light-grey); + border-radius: 2px; + padding: 0.25em 0.5em; +} diff --git a/assets/css/sync/time.css b/assets/css/sync/time.css deleted file mode 100644 index bd65f25ca4..0000000000 --- a/assets/css/sync/time.css +++ /dev/null @@ -1,5 +0,0 @@ -.ep-sync-time { - background-color: var(--ep-sync-color-light-grey); - border-radius: 2px; - padding: 0.25em 0.5em; -} diff --git a/assets/js/sync/components/common/date-time.js b/assets/js/sync/components/common/date-time.js index 105d735bd9..2c2f7342e0 100644 --- a/assets/js/sync/components/common/date-time.js +++ b/assets/js/sync/components/common/date-time.js @@ -11,9 +11,9 @@ import { WPElement } from '@wordpress/element'; * @param {string} props.dateTime Date and time. * @returns {WPElement} Component. */ -export default ({ dateTime }) => { +export default ({ dateTime, ...props }) => { return ( -