Skip to content

Commit

Permalink
AI Assistant: Add FeatureControl to Write Brief (#39168)
Browse files Browse the repository at this point in the history
* add features control to ai-assistant-feature endpoint

* check for write brief feature control

* fix FeatureControl type

* apply feature control checks

* changelog

* fix type

* change default to enabled

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

Upstream-Ref: Automattic/jetpack@77b23eb
  • Loading branch information
dhasilva authored and matticbot committed Aug 30, 2024
1 parent b23ca1a commit 7c19fbb
Show file tree
Hide file tree
Showing 19 changed files with 119 additions and 70 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This is an alpha version! The changes listed here are not final.
- Add retry event for Brief
- AI Assistant: Add A8c dictionary
- AI Assistant: add AI Guidelines link to the AI Assistant sidebar panel
- AI Assistant: Add FeatureControl to Write Brief
- AI Assistant: Add option to add word to spelling dictionary
- AI Assistant: Do not mark words starting with special characters as spelling mistakes
- AI Assistant: Fix flickering when adding word to Breve dictionary
Expand Down
2 changes: 1 addition & 1 deletion _inc/blocks/editor-beta.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('jetpack-script-data', 'lodash', 'moment', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-block-serialization-default-parser', 'wp-blocks', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-media-utils', 'wp-notices', 'wp-plugins', 'wp-polyfill', 'wp-primitives', 'wp-rich-text', 'wp-token-list', 'wp-url', 'wp-viewport', 'wp-widgets', 'wp-wordcount'), 'version' => '085a56f80b46ffafb632');
<?php return array('dependencies' => array('jetpack-script-data', 'lodash', 'moment', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-block-serialization-default-parser', 'wp-blocks', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-media-utils', 'wp-notices', 'wp-plugins', 'wp-polyfill', 'wp-primitives', 'wp-rich-text', 'wp-token-list', 'wp-url', 'wp-viewport', 'wp-widgets', 'wp-wordcount'), 'version' => '980be369b3a62d973a9c');
8 changes: 4 additions & 4 deletions _inc/blocks/editor-beta.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion _inc/blocks/editor-experimental.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('jetpack-script-data', 'lodash', 'moment', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-block-serialization-default-parser', 'wp-blocks', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-media-utils', 'wp-notices', 'wp-plugins', 'wp-polyfill', 'wp-primitives', 'wp-rich-text', 'wp-token-list', 'wp-url', 'wp-viewport', 'wp-widgets', 'wp-wordcount'), 'version' => 'a303f3034c09a20f7b0e');
<?php return array('dependencies' => array('jetpack-script-data', 'lodash', 'moment', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-block-serialization-default-parser', 'wp-blocks', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-media-utils', 'wp-notices', 'wp-plugins', 'wp-polyfill', 'wp-primitives', 'wp-rich-text', 'wp-token-list', 'wp-url', 'wp-viewport', 'wp-widgets', 'wp-wordcount'), 'version' => '05c94fb5ccf9e1ab359f');
8 changes: 4 additions & 4 deletions _inc/blocks/editor-experimental.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion _inc/blocks/editor-no-post-editor.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('jetpack-script-data', 'lodash', 'moment', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-block-serialization-default-parser', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-plugins', 'wp-polyfill', 'wp-primitives', 'wp-token-list', 'wp-url', 'wp-viewport', 'wp-widgets'), 'version' => '9c18205166b7ad8a5758');
<?php return array('dependencies' => array('jetpack-script-data', 'lodash', 'moment', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-block-serialization-default-parser', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-i18n', 'wp-keycodes', 'wp-notices', 'wp-plugins', 'wp-polyfill', 'wp-primitives', 'wp-token-list', 'wp-url', 'wp-viewport', 'wp-widgets'), 'version' => '5a88c0af3edcc836077a');
4 changes: 2 additions & 2 deletions _inc/blocks/editor-no-post-editor.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion _inc/blocks/editor.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('jetpack-script-data', 'lodash', 'moment', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-block-serialization-default-parser', 'wp-blocks', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-media-utils', 'wp-notices', 'wp-plugins', 'wp-polyfill', 'wp-primitives', 'wp-rich-text', 'wp-token-list', 'wp-url', 'wp-viewport', 'wp-widgets', 'wp-wordcount'), 'version' => '335ddba900ddd130ffc7');
<?php return array('dependencies' => array('jetpack-script-data', 'lodash', 'moment', 'react', 'react-dom', 'react-jsx-runtime', 'wp-a11y', 'wp-api-fetch', 'wp-blob', 'wp-block-editor', 'wp-block-serialization-default-parser', 'wp-blocks', 'wp-commands', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-edit-post', 'wp-editor', 'wp-element', 'wp-escape-html', 'wp-hooks', 'wp-html-entities', 'wp-i18n', 'wp-keycodes', 'wp-media-utils', 'wp-notices', 'wp-plugins', 'wp-polyfill', 'wp-primitives', 'wp-rich-text', 'wp-token-list', 'wp-url', 'wp-viewport', 'wp-widgets', 'wp-wordcount'), 'version' => '0bf7d90e5efc1480a6e4');
8 changes: 4 additions & 4 deletions _inc/blocks/editor.js

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions _inc/lib/class-jetpack-ai-helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,17 @@ public static function get_ai_assistance_feature() {
}
}

if ( ! class_exists( 'WPCOM\Jetpack_AI\Feature_Control' ) ) {
if ( is_readable( WP_CONTENT_DIR . '/lib/jetpack-ai/feature-control.php' ) ) {
require_once WP_CONTENT_DIR . '/lib/jetpack-ai/feature-control.php';
} else {
return new WP_Error(
'jetpack_ai_feature_control_not_found',
__( 'WPCOM\Jetpack_AI\Feature_Control class not found.', 'jetpack' )
);
}
}

// Determine the upgrade type
$upgrade_type = wpcom_is_vip( $blog_id ) ? 'vip' : 'default';

Expand All @@ -383,6 +394,7 @@ public static function get_ai_assistance_feature() {
'tier-plans' => WPCOM\Jetpack_AI\Usage\Helper::get_tier_plans_list(),
'tier-plans-enabled' => WPCOM\Jetpack_AI\Usage\Helper::ai_tier_plans_enabled(),
'costs' => WPCOM\Jetpack_AI\Usage\Helper::get_costs(),
'features-control' => WPCOM\Jetpack_AI\Feature_Control::get_features(),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import JetpackPluginSidebar from '../../../../shared/jetpack-plugin-sidebar';
import { PLAN_TYPE_FREE, PLAN_TYPE_UNLIMITED, usePlanType } from '../../../../shared/use-plan-type';
import { FeaturedImage } from '../ai-image';
import { Breve, registerBreveHighlights, Highlight } from '../breve';
import useBreveAvailability from '../breve/hooks/use-breve-availability';
import { getBreveAvailability, canWriteBriefBeEnabled } from '../breve/utils/get-availability';
import Feedback from '../feedback';
import TitleOptimization from '../title-optimization';
import UsagePanel from '../usage-panel';
Expand Down Expand Up @@ -70,7 +70,7 @@ const JetpackAndSettingsContent = ( {
}: JetpackSettingsContentProps ) => {
const { checkoutUrl } = useAICheckout();
const { productPageUrl } = useAiProductPage();
const isBreveAvailable = useBreveAvailability();
const isBreveAvailable = getBreveAvailability();

const currentTitleOptimizationSectionLabel = __( 'Optimize Publishing', 'jetpack' );
const SEOTitleOptimizationSectionLabel = __( 'Optimize Title', 'jetpack' );
Expand All @@ -88,7 +88,7 @@ const JetpackAndSettingsContent = ( {
</PanelRow>
) }

{ isBreveAvailable && (
{ canWriteBriefBeEnabled() && isBreveAvailable && (
<PanelRow>
<BaseControl label={ __( 'Write Brief with AI (BETA)', 'jetpack' ) }>
<Breve />
Expand Down Expand Up @@ -153,7 +153,7 @@ export default function AiAssistantPluginSidebar() {
useAiFeature();
const { checkoutUrl } = useAICheckout();
const { tracks } = useAnalytics();
const isBreveAvailable = useBreveAvailability();
const isBreveAvailable = getBreveAvailability();

const isViewable = useSelect( select => {
const postTypeName = ( select( editorStore ) as typeof EditorSelectors ).getCurrentPostType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import React, { useState, useEffect, useCallback } from 'react';
*/
import features from './features';
import calculateFleschKincaid from './utils/FleschKincaidUtils';
import { canWriteBriefFeatureBeEnabled } from './utils/get-availability';
import { getPostText } from './utils/getPostText';
import './breve.scss';
/**
Expand Down Expand Up @@ -134,17 +135,20 @@ const Controls = ( { blocks, disabledFeatures } ) => {
label={ __( 'Show suggestions', 'jetpack' ) }
/>
<div className="feature-checkboxes-container">
{ features.map( feature => (
<CheckboxControl
className={ isProofreadEnabled ? '' : 'is-disabled' }
disabled={ ! isProofreadEnabled }
data-breve-type={ feature.config.name }
key={ feature.config.name }
label={ feature.config.title }
checked={ ! disabledFeatures.includes( feature.config.name ) }
onChange={ handleToggleFeature( feature.config.name ) }
/>
) ) }
{ features.map(
feature =>
canWriteBriefFeatureBeEnabled( feature.config.name ) && (
<CheckboxControl
className={ isProofreadEnabled ? '' : 'is-disabled' }
disabled={ ! isProofreadEnabled }
data-breve-type={ feature.config.name }
key={ feature.config.name }
label={ feature.config.title }
checked={ ! disabledFeatures.includes( feature.config.name ) }
onChange={ handleToggleFeature( feature.config.name ) }
/>
)
) }
</div>
</BaseControl>
</PanelRow>
Expand Down

This file was deleted.

15 changes: 15 additions & 0 deletions extensions/plugins/ai-assistant-plugin/components/breve/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ export type BreveDispatch = {
} ) => void;
};

export type PlansSelect = {
getAiAssistantFeature: () => {
featuresControl: { [ key: string ]: FeatureControl };
currentTier?: {
value?: number;
};
};
};

export type BreveFeatureConfig = {
name: string;
title: string;
Expand Down Expand Up @@ -127,3 +136,9 @@ export type SpellingDictionaryContext = {
affix: string;
dictionary: string;
};

export type FeatureControl = {
enabled: boolean;
'min-jetpack-version': string;
[ key: string ]: FeatureControl | boolean | string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,30 @@ import { select } from '@wordpress/data';
* Internal dependencies
*/
import { getFeatureAvailability } from '../../../../../blocks/ai-assistant/lib/utils/get-feature-availability';
/**
* Types
*/
import type { FeatureControl, PlansSelect } from '../types';

function getAiAssistantFeature() {
const { getAiAssistantFeature: getFeature } = select( 'wordpress-com/plans' ) as PlansSelect;

return getFeature();
}

export function getBreveAvailability() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { currentTier, featuresControl } = getAiAssistantFeature();

// Disabled remotely.
if ( featuresControl?.[ 'write-brief' ]?.enabled === false ) {
return false;
}

export function getBreveAvailability( _isFreePlan: boolean ) {
// Free plan users have access to Breve while it's in beta.
// const isFreePlan = currentTier?.value === 0;
// TODO: Review this logic when Breve is out of beta.
// if ( _isFreePlan ) {
// if ( isFreePlan ) {
// return false;
// }

Expand All @@ -26,4 +45,16 @@ export function getBreveAvailability( _isFreePlan: boolean ) {
return getFeatureAvailability( 'ai-proofread-breve' );
}

export function canWriteBriefBeEnabled() {
const { featuresControl } = getAiAssistantFeature();

return featuresControl?.[ 'write-brief' ]?.enabled !== false;
}

export function canWriteBriefFeatureBeEnabled( feature: string ) {
const { featuresControl } = getAiAssistantFeature();

return ( featuresControl?.[ 'write-brief' ]?.[ feature ] as FeatureControl )?.enabled !== false;
}

export default getBreveAvailability;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import md5 from 'crypto-js/md5';
import features from '../features';
import registerEvents from '../features/events';
import highlight from '../highlight/highlight';
import getBreveAvailability from '../utils/get-availability';
import {
getBreveAvailability,
canWriteBriefBeEnabled,
canWriteBriefFeatureBeEnabled,
} from '../utils/get-availability';
/**
* Types
*/
Expand Down Expand Up @@ -53,12 +57,12 @@ export function registerBreveHighlight( feature: BreveFeature ) {
getReloadFlag,
} = select( 'jetpack/ai-breve' ) as BreveSelect;

const { getAiAssistantFeature } = select( 'wordpress-com/plans' );
const isFreePlan = getAiAssistantFeature().currentTier?.value === 0;
const canBeEnabled = canWriteBriefBeEnabled();
const canFeatureBeEnabled = canWriteBriefFeatureBeEnabled( config.name );

return {
isProofreadEnabled: isProofreadEnabled() && getBreveAvailability( isFreePlan ),
isFeatureEnabled: isFeatureEnabled( config.name ),
isProofreadEnabled: canBeEnabled && isProofreadEnabled() && getBreveAvailability(),
isFeatureEnabled: canFeatureBeEnabled && isFeatureEnabled( config.name ),
ignored: getIgnoredSuggestions( { blockId: blockClientId } ),
isFeatureDictionaryLoading: isFeatureDictionaryLoading( config.name ),
reloadFlag: getReloadFlag(), // Used to force a reload of the highlights
Expand Down
1 change: 1 addition & 0 deletions extensions/store/wordpress-com/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function mapAiFeatureResponseToAiFeatureProps(
nextTier: response[ 'next-tier' ],
tierPlansEnabled: !! response[ 'tier-plans-enabled' ],
costs: response.costs,
featuresControl: response[ 'features-control' ],
};
}

Expand Down
9 changes: 9 additions & 0 deletions extensions/store/wordpress-com/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ export type TierValueProp =
| Tier750Props[ 'value' ]
| Tier1000Props[ 'value' ];

export type FeatureControl = {
enabled: boolean;
'min-jetpack-version': string;
[ key: string ]: FeatureControl | boolean | string;
};

export type FeaturesControl = { [ key: string ]: FeatureControl };

export type AiFeatureProps = {
hasFeature: boolean;
isOverLimit: boolean;
Expand All @@ -105,6 +113,7 @@ export type AiFeatureProps = {
[ key: string ]: number;
};
};
featuresControl?: FeaturesControl;
};

// Type used in the `wordpress-com/plans` store.
Expand Down
3 changes: 2 additions & 1 deletion extensions/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Types for the AI Assistant feature.
*/
import type { TierProp, UpgradeTypeProp } from './store/wordpress-com/types';
import type { TierProp, UpgradeTypeProp, FeaturesControl } from './store/wordpress-com/types';

/*
* `sites/$site/ai-assistant-feature` endpoint response body props
Expand Down Expand Up @@ -29,4 +29,5 @@ export type SiteAIAssistantFeatureEndpointResponseProps = {
[ key: string ]: number;
};
};
'features-control'?: FeaturesControl;
};

0 comments on commit 7c19fbb

Please sign in to comment.