Skip to content

Commit

Permalink
Update/conditionally hide connection banner (#37707)
Browse files Browse the repository at this point in the history
* Add mechanism to track previously working plugins

* Update project version

* Rework how and when list is updated

* Update array update on my jetpack and get only array values for the array

* Remove new action

* Link connection banner to broken modules data

* Separate broken modules into site and user connection issues

* Fix a few issues with determining user vs site connection

* changelog

* Show connection banner as info conditionally

* Add constants for statuses

* Show errors before info

* Add additional tracking fields to notice if red bubble slug has additional info

* Change how additional tracks args are added

* Add all_statuses variable

* Early return red bubble connection slug if possible

Committed via a GitHub action: https://github.com/Automattic/jetpack/actions/runs/9454902317

Upstream-Ref: Automattic/jetpack@246299d
  • Loading branch information
CodeyGuyDylan authored and matticbot committed Jun 10, 2024
1 parent 2063b95 commit 598cfff
Show file tree
Hide file tree
Showing 13 changed files with 284 additions and 162 deletions.
1 change: 1 addition & 0 deletions jetpack_vendor/automattic/jetpack-my-jetpack/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This is an alpha version! The changes listed here are not final.

### Changed
- Change codebase to use clsx instead of classnames.
- Conditionally show connection banner as error or info
- Updated package dependencies.

## [4.24.4] - 2024-06-10
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => '4443e3857f2017fbb7b4');
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-primitives', 'wp-url'), 'version' => 'a183f6b00b866594faf9');
54 changes: 27 additions & 27 deletions jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion jetpack_vendor/automattic/jetpack-my-jetpack/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ interface Window {
videoPressStats: boolean;
};
lifecycleStats: {
historicallyActiveModules: Array< string >;
brokenModules: Array< string >;
isSiteConnected: boolean;
isUserConnected: boolean;
jetpackPlugins: Array< string >;
Expand Down Expand Up @@ -195,7 +197,10 @@ interface Window {
} >;
};
redBubbleAlerts: {
'missing-site-connection'?: null;
'missing-connection'?: {
type: string;
is_error: boolean;
};
'welcome-banner-active'?: null;
[ key: `${ string }-bad-installation` ]: {
data: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class Initializer {

const UPDATE_HISTORICALLY_ACTIVE_JETPACK_MODULES_KEY = 'update-historically-active-jetpack-modules';

const MISSING_SITE_CONNECTION_NOTIFICATION_KEY = 'missing-site-connection';
const MISSING_CONNECTION_NOTIFICATION_KEY = 'missing-connection';

/**
* Holds info/data about the site (from the /sites/%d endpoint)
Expand Down Expand Up @@ -238,6 +238,7 @@ public static function enqueue_scripts() {
'lifecycleStats' => array(
'jetpackPlugins' => self::get_installed_jetpack_plugins(),
'historicallyActiveModules' => \Jetpack_Options::get_option( 'historically_active_modules', array() ),
'brokenModules' => self::check_for_broken_modules(),
'isSiteConnected' => $connection->is_connected(),
'isUserConnected' => $connection->is_user_connected(),
'purchases' => self::get_purchases(),
Expand Down Expand Up @@ -538,46 +539,27 @@ public static function setup_historically_active_jetpack_modules_sync() {
public static function update_historically_active_jetpack_modules() {
$historically_active_modules = \Jetpack_Options::get_option( 'historically_active_modules', array() );
$products = Products::get_products();
$active_module_statuses = array(
'active',
'can_upgrade',
);
$broken_module_statuses = array(
'site_connection_error',
'user_connection_error',
);
// This is defined as the statuses in which the user willingly has the module disabled whether it be by
// default, uninstalling the plugin, disabling the module, or not renewing their plan.
$disabled_module_statuses = array(
'inactive',
'module_disabled',
'plugin_absent',
'plugin_absent_with_plan',
'needs_purchase',
'needs_purchase_or_free',
'needs_first_site_connection',
);

foreach ( $products as $product ) {
$status = $product['status'];
$product_slug = $product['slug'];
// We want to leave modules in the array if they've been active in the past
// and were not manually disabled by the user.
if ( in_array( $status, $broken_module_statuses, true ) ) {
if ( in_array( $status, Products::$broken_module_statuses, true ) ) {
continue;
}

// If the module is active and not already in the array, add it
if (
in_array( $status, $active_module_statuses, true ) &&
in_array( $status, Products::$active_module_statuses, true ) &&
! in_array( $product_slug, $historically_active_modules, true )
) {
$historically_active_modules[] = $product_slug;
}

// If the module has been disabled due to a manual user action,
// or because of a missing plan error, remove it from the array
if ( in_array( $status, $disabled_module_statuses, true ) ) {
if ( in_array( $status, Products::$disabled_module_statuses, true ) ) {
$historically_active_modules = array_values( array_diff( $historically_active_modules, array( $product_slug ) ) );
}
}
Expand Down Expand Up @@ -668,7 +650,7 @@ public static function dismiss_welcome_banner() {
/**
* Returns true if the site has file write access to the plugins folder, false otherwise.
*
* @return bool
* @return string
**/
public static function has_file_system_write_access() {

Expand Down Expand Up @@ -756,6 +738,34 @@ public static function get_red_bubble_alerts() {
return $red_bubble_alerts;
}

/**
* Check for features broken by a disconnected user or site
*
* @return array
*/
public static function check_for_broken_modules() {
$broken_modules = array(
'needs_site_connection' => array(),
'needs_user_connection' => array(),
);
$products = Products::get_products();
$historically_active_modules = \Jetpack_Options::get_option( 'historically_active_modules', array() );

foreach ( $historically_active_modules as $module ) {
$product = $products[ $module ];

// If the site or user is disconnected, and the product requires a user connection
// mark the product as a broken module needing user connection
if ( in_array( $product['status'], Products::$broken_module_statuses, true ) && $product['requires_user_connection'] ) {
$broken_modules['needs_user_connection'][] = $module;
} elseif ( $product['status'] === Products::STATUS_SITE_CONNECTION_ERROR ) {
$broken_modules['needs_site_connection'][] = $module;
}
}

return $broken_modules;
}

/**
* Add relevant red bubble notifications
*
Expand All @@ -768,7 +778,7 @@ public static function add_red_bubble_alerts( array $red_bubble_slugs ) {
$red_bubble_slugs['welcome-banner-active'] = null;
return $red_bubble_slugs;
} else {
return self::alert_if_missing_site_connection( $red_bubble_slugs );
return self::alert_if_missing_connection( $red_bubble_slugs );
}
}

Expand All @@ -778,9 +788,42 @@ public static function add_red_bubble_alerts( array $red_bubble_slugs ) {
* @param array $red_bubble_slugs - slugs that describe the reasons the red bubble is showing.
* @return array
*/
public static function alert_if_missing_site_connection( array $red_bubble_slugs ) {
public static function alert_if_missing_connection( array $red_bubble_slugs ) {
$broken_modules = self::check_for_broken_modules();

if ( ! empty( $broken_modules['needs_user_connection'] ) ) {
$red_bubble_slugs[ self::MISSING_CONNECTION_NOTIFICATION_KEY ] = array(
'type' => 'user',
'is_error' => true,
);
return $red_bubble_slugs;
}

if ( ! empty( $broken_modules['needs_site_connection'] ) ) {
$red_bubble_slugs[ self::MISSING_CONNECTION_NOTIFICATION_KEY ] = array(
'type' => 'site',
'is_error' => true,
);
return $red_bubble_slugs;
}

if (
! ( new Connection_Manager() )->is_user_connected() &&
! ( new Connection_Manager() )->has_connected_owner()
) {
$red_bubble_slugs[ self::MISSING_CONNECTION_NOTIFICATION_KEY ] = array(
'type' => 'user',
'is_error' => false,
);
return $red_bubble_slugs;
}

if ( ! ( new Connection_Manager() )->is_connected() ) {
$red_bubble_slugs[ self::MISSING_SITE_CONNECTION_NOTIFICATION_KEY ] = null;
$red_bubble_slugs[ self::MISSING_CONNECTION_NOTIFICATION_KEY ] = array(
'type' => 'site',
'is_error' => false,
);
return $red_bubble_slugs;
}

return $red_bubble_slugs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,78 @@
* A class for everything related to product handling in My Jetpack
*/
class Products {
/**
* Constants for the status of a product on a site
*
* @var string
*/
const STATUS_SITE_CONNECTION_ERROR = 'site_connection_error';
const STATUS_USER_CONNECTION_ERROR = 'user_connection_error';
const STATUS_ACTIVE = 'active';
const STATUS_CAN_UPGRADE = 'can_upgrade';
const STATUS_INACTIVE = 'inactive';
const STATUS_MODULE_DISABLED = 'module_disabled';
const STATUS_PLUGIN_ABSENT = 'plugin_absent';
const STATUS_PLUGIN_ABSENT_WITH_PLAN = 'plugin_absent_with_plan';
const STATUS_NEEDS_PURCHASE = 'needs_purchase';
const STATUS_NEEDS_PURCHASE_OR_FREE = 'needs_purchase_or_free';
const STATUS_NEEDS_FIRST_SITE_CONNECTION = 'needs_first_site_connection';

/**
* List of statuses that display the module as disabled
* This is defined as the statuses in which the user willingly has the module disabled whether it be by
* default, uninstalling the plugin, disabling the module, or not renewing their plan.
*
* @var array
*/
public static $disabled_module_statuses = array(
self::STATUS_INACTIVE,
self::STATUS_MODULE_DISABLED,
self::STATUS_PLUGIN_ABSENT,
self::STATUS_PLUGIN_ABSENT_WITH_PLAN,
self::STATUS_NEEDS_PURCHASE,
self::STATUS_NEEDS_PURCHASE_OR_FREE,
self::STATUS_NEEDS_FIRST_SITE_CONNECTION,
);

/**
* List of statuses that display the module as broken
*
* @var array
*/
public static $broken_module_statuses = array(
self::STATUS_SITE_CONNECTION_ERROR,
self::STATUS_USER_CONNECTION_ERROR,
);

/**
* List of statuses that display the module as active
*
* @var array
*/
public static $active_module_statuses = array(
self::STATUS_ACTIVE,
self::STATUS_CAN_UPGRADE,
);

/**
* List of all statuses that a product can have
*
* @var array
*/
public static $all_statuses = array(
self::STATUS_SITE_CONNECTION_ERROR,
self::STATUS_USER_CONNECTION_ERROR,
self::STATUS_ACTIVE,
self::STATUS_CAN_UPGRADE,
self::STATUS_INACTIVE,
self::STATUS_MODULE_DISABLED,
self::STATUS_PLUGIN_ABSENT,
self::STATUS_PLUGIN_ABSENT_WITH_PLAN,
self::STATUS_NEEDS_PURCHASE,
self::STATUS_NEEDS_PURCHASE_OR_FREE,
self::STATUS_NEEDS_FIRST_SITE_CONNECTION,
);

/**
* Get the list of Products classes
Expand Down Expand Up @@ -156,7 +228,7 @@ public static function get_product_data_schema() {
'status' => array(
'title' => 'The product status',
'type' => 'string',
'enum' => array( 'active', 'inactive', 'plugin_absent', 'needs_purchase', 'needs_purchase_or_free', 'needs_first_site_connection', 'user_connection_error', 'site_connection_error' ),
'enum' => self::$all_statuses,
),
'class' => array(
'title' => 'The product class handler',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ public static function is_module_active() {
*/
public static function get_status() {
$status = parent::get_status();
if ( 'inactive' === $status && ! static::is_module_active() ) {
$status = 'module_disabled';
if ( Products::STATUS_INACTIVE === $status && ! static::is_module_active() ) {
$status = Products::STATUS_MODULE_DISABLED;
}
return $status;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ public static function is_upgradable_by_bundle() {
* return all the products it contains.
* Empty array by default.
*
* @return Array Product slugs
* @return array Product slugs
*/
public static function get_supported_products() {
return array();
Expand All @@ -445,48 +445,48 @@ public static function get_supported_products() {
*/
public static function get_status() {
if ( ! static::is_plugin_installed() ) {
$status = 'plugin_absent';
$status = Products::STATUS_PLUGIN_ABSENT;
if ( static::has_paid_plan_for_product() ) {
$status = 'plugin_absent_with_plan';
$status = Products::STATUS_PLUGIN_ABSENT_WITH_PLAN;
}
} elseif ( static::is_active() ) {
$status = 'active';
$status = Products::STATUS_ACTIVE;
// We only consider missing site & user connection an error when the Product is active.
if ( static::$requires_site_connection && ! ( new Connection_Manager() )->is_connected() ) {
// Site has never been connected before
if ( ! \Jetpack_Options::get_option( 'id' ) ) {
$status = 'needs_first_site_connection';
if ( ! Jetpack_Options::get_option( 'id' ) ) {
$status = Products::STATUS_NEEDS_FIRST_SITE_CONNECTION;
} else {
$status = 'site_connection_error';
$status = Products::STATUS_SITE_CONNECTION_ERROR;
}
} elseif ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) {
$status = 'user_connection_error';
$status = Products::STATUS_USER_CONNECTION_ERROR;
} elseif ( static::is_upgradable() ) {
$status = 'can_upgrade';
$status = Products::STATUS_CAN_UPGRADE;
}
// Check specifically for inactive modules, which will prevent a product from being active
} elseif ( static::$module_name && ! static::is_module_active() ) {
$status = 'module_disabled';
$status = Products::STATUS_MODULE_DISABLED;
// If there is not a plan associated with the disabled module, encourage a plan first
// Getting a plan set up should help resolve any connection issues
// However if the standalone plugin for this product is active, then we will defer to showing errors that prevent the module from being active
// This is because if a standalone plugin is installed, we expect the product to not show as "inactive" on My Jetpack
if ( static::$requires_plan || ( ! static::has_any_plan_for_product() && static::$has_standalone_plugin && ! self::is_plugin_active() ) ) {
$status = static::$has_free_offering ? 'needs_purchase_or_free' : 'needs_purchase';
$status = static::$has_free_offering ? Products::STATUS_NEEDS_PURCHASE_OR_FREE : Products::STATUS_NEEDS_PURCHASE;
} elseif ( static::$requires_site_connection && ! ( new Connection_Manager() )->is_connected() ) {
// Site has never been connected before
if ( ! \Jetpack_Options::get_option( 'id' ) ) {
$status = 'needs_first_site_connection';
if ( ! Jetpack_Options::get_option( 'id' ) ) {
$status = Products::STATUS_NEEDS_FIRST_SITE_CONNECTION;
} else {
$status = 'site_connection_error';
$status = Products::STATUS_SITE_CONNECTION_ERROR;
}
} elseif ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) {
$status = 'user_connection_error';
$status = Products::STATUS_USER_CONNECTION_ERROR;
}
} elseif ( ! static::has_any_plan_for_product() ) {
$status = static::$has_free_offering ? 'needs_purchase_or_free' : 'needs_purchase';
$status = static::$has_free_offering ? Products::STATUS_NEEDS_PURCHASE_OR_FREE : Products::STATUS_NEEDS_PURCHASE;
} else {
$status = 'inactive';
$status = Products::STATUS_INACTIVE;
}
return $status;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use Automattic\Jetpack\My_Jetpack\Initializer;
use Automattic\Jetpack\My_Jetpack\Module_Product;
use Automattic\Jetpack\My_jetpack\Products;
use Automattic\Jetpack\My_Jetpack\Wpcom_Products;
use Automattic\Jetpack\Status\Host;
use Jetpack_Options;
Expand Down Expand Up @@ -169,10 +170,10 @@ public static function get_wpcom_free_product_slug() {
*/
public static function get_status() {
$status = parent::get_status();
if ( 'module_disabled' === $status && ! Initializer::is_registered() ) {
if ( Products::STATUS_MODULE_DISABLED === $status && ! Initializer::is_registered() ) {
// If the site has never been connected before, show the "Learn more" CTA,
// that points to the add Stats product interstitial.
$status = 'needs_purchase_or_free';
$status = Products::STATUS_NEEDS_PURCHASE_OR_FREE;
}
return $status;
}
Expand Down
2 changes: 1 addition & 1 deletion jetpack_vendor/i18n-map.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
),
'jetpack-my-jetpack' => array(
'path' => 'jetpack_vendor/automattic/jetpack-my-jetpack',
'ver' => '4.24.5-alpha1718047622',
'ver' => '4.24.5-alpha1718050359',
),
'jetpack-password-checker' => array(
'path' => 'jetpack_vendor/automattic/jetpack-password-checker',
Expand Down
Loading

0 comments on commit 598cfff

Please sign in to comment.