diff --git a/assets/src/dashboard/parts/components/Modal.js b/assets/src/dashboard/parts/components/Modal.js index c3a5d1e04..9318e23b9 100644 --- a/assets/src/dashboard/parts/components/Modal.js +++ b/assets/src/dashboard/parts/components/Modal.js @@ -4,7 +4,7 @@ import { close } from '@wordpress/icons'; import { useViewportMatch } from '@wordpress/compose'; import { Button, Icon, Modal as CoreModal } from '@wordpress/components'; -export default function Modal({ icon, labels = {}, onRequestClose = () => {}, onConfirm = () => {}, variant = 'default', onSecondaryAction = () => {} }) { +export default function Modal({ icon, labels = {}, onRequestClose = () => {}, onConfirm = () => {}, variant = 'default', onSecondaryAction = () => {}, afterContentChildren = null }) { const isMobileViewport = useViewportMatch( 'small', '<' ); @@ -53,7 +53,8 @@ export default function Modal({ icon, labels = {}, onRequestClose = () => {}, on className="text-center mx-0 my-4 text-gray-700" dangerouslySetInnerHTML={ { __html: labels.description } } /> -
+ { afterContentChildren } +
diff --git a/assets/src/dashboard/parts/connected/settings/OffloadMedia.js b/assets/src/dashboard/parts/connected/settings/OffloadMedia.js index f098efb47..896c91020 100644 --- a/assets/src/dashboard/parts/connected/settings/OffloadMedia.js +++ b/assets/src/dashboard/parts/connected/settings/OffloadMedia.js @@ -1,7 +1,7 @@ /* global optimoleDashboardApp */ import classnames from 'classnames'; -import { useEffect, useState } from '@wordpress/element'; +import { useEffect, useState, useMemo } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { Icon } from '@wordpress/icons'; @@ -37,7 +37,9 @@ const OffloadMedia = ({ settings, canSave, setSettings, setCanSave }) => { processedImages, offloadFinishNotice, offloadLimitReached, - offloadLimit + offloadLimit, + canUseMediaOffloadingFlag, + canShowFreeUserWithOffloadNoticeFlag } = useSelect( select => { const { getOffloadConflicts, @@ -51,9 +53,13 @@ const OffloadMedia = ({ settings, canSave, setSettings, setCanSave }) => { getQueryArgs, isLoading, getSiteSettings, - getOffloadLimit + getOffloadLimit, + getUserData, + canShowFreeUserWithOffloadNotice } = select( 'optimole' ); + const userData = getUserData(); + return { offloadConflicts: getOffloadConflicts(), errorMedia: getErrorMedia(), @@ -67,7 +73,9 @@ const OffloadMedia = ({ settings, canSave, setSettings, setCanSave }) => { processedImages: getProcessedImages(), offloadFinishNotice: getSiteSettings( 'show_offload_finish_notice' ), offloadLimitReached: 'enabled' === getSiteSettings( 'offload_limit_reached' ), - offloadLimit: getOffloadLimit() + offloadLimit: getOffloadLimit(), + canUseMediaOffloadingFlag: Boolean( userData?.can_use_offloading ), + canShowFreeUserWithOffloadNoticeFlag: canShowFreeUserWithOffloadNotice() }; }, []); @@ -85,6 +93,9 @@ const OffloadMedia = ({ settings, canSave, setSettings, setCanSave }) => { const isOffloadingInProgress = 'disabled' !== settings['offloading_status']; const isRollbackInProgress = 'disabled' !== settings['rollback_status']; + const showDisabledOffloadingNotice = useMemo( () => { + return 'enabled' === settings?.['offload_media'] && ! canUseMediaOffloadingFlag && canShowFreeUserWithOffloadNoticeFlag; + }, [ settings, canUseMediaOffloadingFlag, canShowFreeUserWithOffloadNoticeFlag ]); useEffect( () => { if ( isOffloadingInProgress ) { @@ -187,18 +198,28 @@ const OffloadMedia = ({ settings, canSave, setSettings, setCanSave }) => { [MODAL_STATE_OFFLOAD]: { icon: offload, onConfirm: () => { - nextSettings['offload_media'] = 'offload' === optionValue ? 'enabled' : 'disabled'; - setSettings( nextSettings ); - setCanSave( true ); - - onOffloadMedia(); + if ( canUseMediaOffloadingFlag ) { + nextSettings['offload_media'] = 'offload' === optionValue ? 'enabled' : 'disabled'; + setSettings( nextSettings ); + setCanSave( true ); + + onOffloadMedia(); + } else { + window.open( optimoleDashboardApp?.offload_upgrade_url, '_blank' ); + } setModal( null ); }, labels: { title: options_strings.offloading_start_title, description: options_strings.offloading_start_description, - action: options_strings.offloading_start_action - } + action: canUseMediaOffloadingFlag ? options_strings.offloading_start_action : optimoleDashboardApp.strings.upgrade.title_long + }, + afterContentChildren: ! canUseMediaOffloadingFlag && ( + + ) }, [MODAL_STATE_ROLLBACK]: { icon: rollbackIcon, @@ -322,6 +343,16 @@ const OffloadMedia = ({ settings, canSave, setSettings, setCanSave }) => {

{options_strings.enable_offload_media_title}

+ { + showDisabledOffloadingNotice && ( + + ) + } + {offloadFinishNotice && ( { diff --git a/assets/src/dashboard/store/selectors.js b/assets/src/dashboard/store/selectors.js index deb5b3436..8bda6cc4f 100644 --- a/assets/src/dashboard/store/selectors.js +++ b/assets/src/dashboard/store/selectors.js @@ -96,6 +96,9 @@ const selectors = { }, isSubApiKey( state ) { return state.apiKey && state.apiKey.startsWith( 'optml-s' ); + }, + canShowFreeUserWithOffloadNotice( state ) { + return Boolean( state?.canShowFreeUserWithOffloadNotice ); } }; diff --git a/inc/admin.php b/inc/admin.php index b5cabe68a..0121a0769 100755 --- a/inc/admin.php +++ b/inc/admin.php @@ -113,6 +113,7 @@ public function __construct() { } add_filter( 'themeisle-sdk/survey/' . OPTML_PRODUCT_SLUG, [ $this, 'get_survey_metadata' ], 10, 2 ); + add_action( 'admin_init', [ $this, 'mark_user_with_offload' ] ); } /** @@ -1070,8 +1071,16 @@ public function daily_sync() { if ( isset( $data['extra_visits'] ) ) { $this->settings->update_frontend_banner_from_remote( $data['extra_visits'] ); } + // Here the account got deactivated, in this case we check if the user is using offloaded images and we roll them back. - if ( isset( $data['status'] ) && $data['status'] === 'inactive' ) { + $should_revert_offloading = isset( $data['status'] ) && $data['status'] === 'inactive'; + + // The user is now on a plan without offloading and the grace period is over. + if ( isset( $data['should_revert_offload'] ) && (bool) $data['should_revert_offload'] ) { + $should_revert_offloading = true; + } + + if ( $should_revert_offloading ) { // We check if the user has images offloaded. if ( $this->settings->get( 'offload_media' ) === 'disabled' ) { return; @@ -1375,6 +1384,7 @@ private function localize_dashboard_app() { 'optimoleHome' => tsdk_translate_link( 'https://optimole.com/' ), 'optimoleDashHome' => tsdk_translate_link( 'https://dashboard.optimole.com/', 'query' ), 'optimoleDashBilling' => tsdk_translate_link( 'https://dashboard.optimole.com/settings/billing', 'query' ), + 'offload_upgrade_url' => tsdk_translate_link( tsdk_utmify( 'https://optimole.com/pricing/', 'offload' ) ), 'days_since_install' => round( ( time() - get_option( 'optimole_wp_install', 0 ) ) / DAY_IN_SECONDS ), 'is_offload_media_available' => $is_offload_media_available, 'auto_connect' => $auto_connect, @@ -1389,6 +1399,7 @@ private function localize_dashboard_app() { 'bf_notices' => $this->get_bf_notices(), 'spc_banner' => $this->get_spc_banner(), 'show_exceed_plan_quota_notice' => $this->should_show_exceed_quota_warning(), + 'show_free_user_with_offload_notice' => get_option( 'optml_has_offloading_enabled_on_upgrade', 'no' ), 'report_issue_url' => add_query_arg( [ 'utm_source' => 'plugin', @@ -1626,7 +1637,7 @@ private function get_dashboard_strings() { ' optimole.com' ), 'account_needed_subtitle_1' => sprintf( - /* translators: 1 is starting bold tag, 2 is ending bold tag, 3 is the starting bold tag, 4 is the limit number, 5 is ending bold tag, 6 is the starting anchor tag for the docs link on how we count visits, 7 is the ending anchor tag. */ + /* translators: 1 is the starting bold tag, 2 is the ending bold tag, 3 is the starting bold tag, 4 is the limit number, 5 is ending bold tag, 6 is the starting anchor tag for the docs link on how we count visits, 7 is the ending anchor tag. */ __( '%1$sOptimize unlimited images%2$s for up to %3$s monthly %4$svisitors%5$s - completely FREE.', 'optimole-wp' ), '', '', @@ -2085,6 +2096,9 @@ private function get_dashboard_strings() { 'exceed_plan_quota_notice_description' => sprintf( /* translators: 1 is the starting anchor tag, 2 is the ending anchor tag */ __( 'Based on this trend, you are likely to exceed your free quota before the month ends. To avoid any disruption in service, we strongly recommend %1$supgrading%2$s your plan or waiting until your traffic stabilizes before offloading your images. Do you still wish to proceed?', 'optimole-wp' ), '', '' ), 'exceed_plan_quota_notice_start_action' => __( 'Yes, Transfer to Optimole Cloud', 'optimole-wp' ), 'exceed_plan_quota_notice_secondary_action' => __( 'No, keep images on my website', 'optimole-wp' ), + 'plan_update_notice_title' => __( 'Plan Update', 'optimole-wp' ), + 'plan_update_notice_desc' => __( 'We\'ve changed how plans work. Users on the Optimole Free plan cannot offload new images. Existing images that are already offloaded will remain offloaded.', 'optimole-wp' ), + 'upgrade_to_use_offloading_notice_desc' => __( 'Offloading images is a PRO feature. Please upgrade your plan to enable image transfer to Optimole Cloud.', 'optimole-wp' ), 'visual_settings' => __( 'Visual Settings', 'optimole-wp' ), 'extended_features' => __( 'Extended Features', 'optimole-wp' ), // translators: mark that the options are aplied globally. @@ -2376,4 +2390,23 @@ private function get_active_notices_count() { return $conflicts_count - count( $dismissed_notices ); } + + /** + * Mark if the user had offloading enabled on first run. + * + * If it is an old free user that had offloading enabled, we will use the mark to show a notice about the plan changes. + * + * @return void + */ + public function mark_user_with_offload() { + if ( ! $this->settings->is_connected() ) { + return; + } + + if ( false !== get_option( 'optml_has_offloading_enabled_on_upgrade', false ) ) { + return; + } + + update_option( 'optml_has_offloading_enabled_on_upgrade', $this->settings->is_offload_enabled() ? 'yes' : 'no' ); + } } diff --git a/inc/main.php b/inc/main.php index cbf1f93d3..c0f9854b2 100644 --- a/inc/main.php +++ b/inc/main.php @@ -7,7 +7,7 @@ final class Optml_Main { /** * Optml_Main The single instance of Starter_Plugin. * - * @var object + * @var Optml_Main|null * @access private * @since 1.0.0 */