@automattic/jetpack-ai-client 0.16.4 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/build/components/ai-control/extension-ai-control.d.ts +2 -1
- package/build/components/ai-control/extension-ai-control.js +5 -2
- package/build/components/message/index.d.ts +6 -0
- package/build/components/message/index.js +13 -0
- package/build/jwt/index.js +1 -1
- package/build/logo-generator/components/fair-usage-notice.d.ts +11 -0
- package/build/logo-generator/components/fair-usage-notice.js +19 -0
- package/build/logo-generator/components/generator-modal.js +13 -6
- package/build/logo-generator/components/history-carousel.js +6 -5
- package/build/logo-generator/components/logo-presenter.js +8 -3
- package/build/logo-generator/components/prompt.js +5 -4
- package/build/logo-generator/hooks/use-checkout.js +3 -2
- package/build/logo-generator/hooks/use-fair-usage-notice-message.d.ts +3 -0
- package/build/logo-generator/hooks/use-fair-usage-notice-message.js +43 -0
- package/build/logo-generator/hooks/use-logo-generator.d.ts +3 -0
- package/build/logo-generator/hooks/use-logo-generator.js +7 -2
- package/build/logo-generator/store/actions.d.ts +4 -0
- package/build/logo-generator/store/actions.js +8 -1
- package/build/logo-generator/store/constants.d.ts +1 -0
- package/build/logo-generator/store/constants.js +1 -0
- package/build/logo-generator/store/reducer.d.ts +37 -0
- package/build/logo-generator/store/reducer.js +10 -1
- package/build/logo-generator/store/selectors.d.ts +14 -0
- package/build/logo-generator/store/selectors.js +21 -0
- package/build/logo-generator/store/types.d.ts +13 -0
- package/package.json +13 -13
- package/src/components/ai-control/extension-ai-control.tsx +10 -1
- package/src/components/message/index.tsx +20 -0
- package/src/jwt/index.ts +1 -2
- package/src/logo-generator/components/fair-usage-notice.tsx +38 -0
- package/src/logo-generator/components/generator-modal.tsx +24 -7
- package/src/logo-generator/components/history-carousel.tsx +8 -1
- package/src/logo-generator/components/logo-presenter.tsx +16 -5
- package/src/logo-generator/components/prompt.tsx +8 -4
- package/src/logo-generator/hooks/use-checkout.ts +7 -2
- package/src/logo-generator/hooks/use-fair-usage-notice-message.tsx +68 -0
- package/src/logo-generator/hooks/use-logo-generator.ts +8 -0
- package/src/logo-generator/store/actions.ts +9 -0
- package/src/logo-generator/store/constants.ts +1 -0
- package/src/logo-generator/store/reducer.ts +12 -0
- package/src/logo-generator/store/selectors.ts +24 -0
- package/src/logo-generator/store/types.ts +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.18.0] - 2024-09-09
|
|
9
|
+
### Added
|
|
10
|
+
- AI Client: add placeholders for Logo Generator modal commponents [#39244]
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- AI Logo generator: add over quota notice, handle disabling tiers on checkout [#39149]
|
|
14
|
+
- Updated package dependencies. [#39176]
|
|
15
|
+
|
|
16
|
+
## [0.17.0] - 2024-09-02
|
|
17
|
+
### Added
|
|
18
|
+
- AI Client: Add FeaturesControl to ai-assistant-feature response parsing. [#39168]
|
|
19
|
+
- Jetpack AI: Support fair usage messaging on the Extension AI Control component. [#39103]
|
|
20
|
+
|
|
8
21
|
## [0.16.4] - 2024-08-26
|
|
9
22
|
### Changed
|
|
10
23
|
- Updated package dependencies. [#39004]
|
|
@@ -392,6 +405,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
392
405
|
- Updated package dependencies. [#31659]
|
|
393
406
|
- Updated package dependencies. [#31785]
|
|
394
407
|
|
|
408
|
+
[0.18.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.17.0...v0.18.0
|
|
409
|
+
[0.17.0]: https://github.com/Automattic/jetpack-ai-client/compare/v0.16.4...v0.17.0
|
|
395
410
|
[0.16.4]: https://github.com/Automattic/jetpack-ai-client/compare/v0.16.3...v0.16.4
|
|
396
411
|
[0.16.3]: https://github.com/Automattic/jetpack-ai-client/compare/v0.16.2...v0.16.3
|
|
397
412
|
[0.16.2]: https://github.com/Automattic/jetpack-ai-client/compare/v0.16.1...v0.16.2
|
|
@@ -17,6 +17,7 @@ type ExtensionAIControlProps = {
|
|
|
17
17
|
error?: RequestingErrorProps;
|
|
18
18
|
requestsRemaining?: number;
|
|
19
19
|
showUpgradeMessage?: boolean;
|
|
20
|
+
showFairUsageMessage?: boolean;
|
|
20
21
|
upgradeUrl?: string;
|
|
21
22
|
wrapperRef?: React.MutableRefObject<HTMLDivElement | null>;
|
|
22
23
|
onChange?: (newValue: string) => void;
|
|
@@ -34,6 +35,6 @@ type ExtensionAIControlProps = {
|
|
|
34
35
|
* @param {React.MutableRefObject} ref - Ref to the component
|
|
35
36
|
* @return {ReactElement} Rendered component
|
|
36
37
|
*/
|
|
37
|
-
export declare function ExtensionAIControl({ className, disabled, value, placeholder, showButtonLabels, isTransparent, state, showGuideLine, error, requestsRemaining, showUpgradeMessage, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }: ExtensionAIControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
|
|
38
|
+
export declare function ExtensionAIControl({ className, disabled, value, placeholder, showButtonLabels, isTransparent, state, showGuideLine, error, requestsRemaining, showUpgradeMessage, showFairUsageMessage, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }: ExtensionAIControlProps, ref: React.MutableRefObject<HTMLInputElement>): ReactElement;
|
|
38
39
|
declare const _default: React.ForwardRefExoticComponent<ExtensionAIControlProps & React.RefAttributes<HTMLInputElement>>;
|
|
39
40
|
export default _default;
|
|
@@ -11,7 +11,7 @@ import { forwardRef } from 'react';
|
|
|
11
11
|
/**
|
|
12
12
|
* Internal dependencies
|
|
13
13
|
*/
|
|
14
|
-
import { GuidelineMessage, ErrorMessage, UpgradeMessage } from '../message/index.js';
|
|
14
|
+
import { GuidelineMessage, ErrorMessage, UpgradeMessage, FairUsageLimitMessage, } from '../message/index.js';
|
|
15
15
|
import AIControl from './ai-control.js';
|
|
16
16
|
import './style.scss';
|
|
17
17
|
/**
|
|
@@ -21,7 +21,7 @@ import './style.scss';
|
|
|
21
21
|
* @param {React.MutableRefObject} ref - Ref to the component
|
|
22
22
|
* @return {ReactElement} Rendered component
|
|
23
23
|
*/
|
|
24
|
-
export function ExtensionAIControl({ className, disabled = false, value = '', placeholder = '', showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, error, requestsRemaining, showUpgradeMessage = false, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }, ref) {
|
|
24
|
+
export function ExtensionAIControl({ className, disabled = false, value = '', placeholder = '', showButtonLabels = true, isTransparent = false, state = 'init', showGuideLine = false, error, requestsRemaining, showUpgradeMessage = false, showFairUsageMessage = false, upgradeUrl, wrapperRef, onChange, onSend, onStop, onClose, onUndo, onUpgrade, onTryAgain, }, ref) {
|
|
25
25
|
const loading = state === 'requesting' || state === 'suggesting';
|
|
26
26
|
const [editRequest, setEditRequest] = useState(false);
|
|
27
27
|
const [lastValue, setLastValue] = useState(value || null);
|
|
@@ -78,6 +78,9 @@ export function ExtensionAIControl({ className, disabled = false, value = '', pl
|
|
|
78
78
|
if (error?.message) {
|
|
79
79
|
message = (_jsx(ErrorMessage, { error: error.message, code: error.code, onTryAgainClick: tryAgainHandler, onUpgradeClick: upgradeHandler, upgradeUrl: upgradeUrl }));
|
|
80
80
|
}
|
|
81
|
+
else if (showFairUsageMessage) {
|
|
82
|
+
message = _jsx(FairUsageLimitMessage, {});
|
|
83
|
+
}
|
|
81
84
|
else if (showUpgradeMessage) {
|
|
82
85
|
message = (_jsx(UpgradeMessage, { requestsRemaining: requestsRemaining, onUpgradeClick: upgradeHandler, upgradeUrl: upgradeUrl }));
|
|
83
86
|
}
|
|
@@ -47,6 +47,12 @@ export default function Message({ severity, icon, showSidebarIcon, onSidebarIcon
|
|
|
47
47
|
* @return {React.ReactElement } - Message component.
|
|
48
48
|
*/
|
|
49
49
|
export declare function GuidelineMessage(): React.ReactElement;
|
|
50
|
+
/**
|
|
51
|
+
* React component to render a fair usage limit message.
|
|
52
|
+
*
|
|
53
|
+
* @return {React.ReactElement } - Message component.
|
|
54
|
+
*/
|
|
55
|
+
export declare function FairUsageLimitMessage(): React.ReactElement;
|
|
50
56
|
/**
|
|
51
57
|
* React component to render an upgrade message for free tier users
|
|
52
58
|
*
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
* External dependencies
|
|
4
4
|
*/
|
|
5
5
|
import { ExternalLink, Button } from '@wordpress/components';
|
|
6
|
+
import { createInterpolateElement } from '@wordpress/element';
|
|
6
7
|
import { __, sprintf } from '@wordpress/i18n';
|
|
7
8
|
import { Icon, check, arrowRight } from '@wordpress/icons';
|
|
8
9
|
import clsx from 'clsx';
|
|
@@ -45,6 +46,18 @@ export default function Message({ severity = MESSAGE_SEVERITY_INFO, icon = null,
|
|
|
45
46
|
export function GuidelineMessage() {
|
|
46
47
|
return (_jsxs(Message, { children: [_jsx("span", { children: __('AI-generated content could be inaccurate or biased.', 'jetpack-ai-client') }), _jsx(ExternalLink, { href: "https://automattic.com/ai-guidelines", children: __('Learn more', 'jetpack-ai-client') })] }));
|
|
47
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* React component to render a fair usage limit message.
|
|
51
|
+
*
|
|
52
|
+
* @return {React.ReactElement } - Message component.
|
|
53
|
+
*/
|
|
54
|
+
export function FairUsageLimitMessage() {
|
|
55
|
+
const message = __("You've reached this month's request limit, per our <link>fair usage policy</link>", 'jetpack-ai-client');
|
|
56
|
+
const element = createInterpolateElement(message, {
|
|
57
|
+
link: (_jsx(ExternalLink, { href: "https://jetpack.com/redirect/?source=ai-assistant-fair-usage-policy" })),
|
|
58
|
+
});
|
|
59
|
+
return _jsx(Message, { severity: MESSAGE_SEVERITY_WARNING, children: element });
|
|
60
|
+
}
|
|
48
61
|
/**
|
|
49
62
|
* React component to render an upgrade message for free tier users
|
|
50
63
|
*
|
package/build/jwt/index.js
CHANGED
|
@@ -21,7 +21,6 @@ export default async function requestJwt({ apiNonce, siteId, expirationTime, } =
|
|
|
21
21
|
apiNonce = apiNonce || window.JP_CONNECTION_INITIAL_STATE.apiNonce;
|
|
22
22
|
siteId = siteId || window.JP_CONNECTION_INITIAL_STATE.siteSuffix;
|
|
23
23
|
expirationTime = expirationTime || JWT_TOKEN_EXPIRATION_TIME;
|
|
24
|
-
const isSimple = isSimpleSite();
|
|
25
24
|
// Trying to pick the token from localStorage
|
|
26
25
|
const token = localStorage.getItem(JWT_TOKEN_ID);
|
|
27
26
|
let tokenData = null;
|
|
@@ -38,6 +37,7 @@ export default async function requestJwt({ apiNonce, siteId, expirationTime, } =
|
|
|
38
37
|
return tokenData;
|
|
39
38
|
}
|
|
40
39
|
let data;
|
|
40
|
+
const isSimple = isSimpleSite();
|
|
41
41
|
if (!isSimple) {
|
|
42
42
|
data = await apiFetch({
|
|
43
43
|
/*
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type FairUsageNoticeProps = {
|
|
2
|
+
variant?: 'error' | 'muted';
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* The fair usage notice component.
|
|
6
|
+
* @param {FairUsageNoticeProps} props - Fair usage notice component props.
|
|
7
|
+
* @param {FairUsageNoticeProps.variant} props.variant - The variant of the notice to render.
|
|
8
|
+
* @return {ReactElement} the Notice component with the fair usage message.
|
|
9
|
+
*/
|
|
10
|
+
export declare const FairUsageNotice: ({ variant }: FairUsageNoticeProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Notice } from '@wordpress/components';
|
|
3
|
+
import useFairUsageNoticeMessage from '../hooks/use-fair-usage-notice-message.js';
|
|
4
|
+
/**
|
|
5
|
+
* The fair usage notice component.
|
|
6
|
+
* @param {FairUsageNoticeProps} props - Fair usage notice component props.
|
|
7
|
+
* @param {FairUsageNoticeProps.variant} props.variant - The variant of the notice to render.
|
|
8
|
+
* @return {ReactElement} the Notice component with the fair usage message.
|
|
9
|
+
*/
|
|
10
|
+
export const FairUsageNotice = ({ variant = 'error' }) => {
|
|
11
|
+
const useFairUsageNoticeMessageElement = useFairUsageNoticeMessage();
|
|
12
|
+
if (variant === 'muted') {
|
|
13
|
+
return (_jsx("span", { className: "jetpack-ai-fair-usage-notice-muted-variant", children: useFairUsageNoticeMessageElement }));
|
|
14
|
+
}
|
|
15
|
+
if (variant === 'error') {
|
|
16
|
+
return (_jsx(Notice, { status: "error", isDismissible: false, className: "jetpack-ai-fair-usage-notice", children: useFairUsageNoticeMessageElement }));
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
@@ -19,6 +19,7 @@ import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
|
19
19
|
import useRequestErrors from '../hooks/use-request-errors.js';
|
|
20
20
|
import { isLogoHistoryEmpty, clearDeletedMedia } from '../lib/logo-storage.js';
|
|
21
21
|
import { STORE_NAME } from '../store/index.js';
|
|
22
|
+
// import { FairUsageNotice } from './fair-usage-notice.js';
|
|
22
23
|
import { FeatureFetchFailureScreen } from './feature-fetch-failure-screen.js';
|
|
23
24
|
import { FirstLoadScreen } from './first-load-screen.js';
|
|
24
25
|
import { HistoryCarousel } from './history-carousel.js';
|
|
@@ -31,7 +32,7 @@ const debug = debugFactory('jetpack-ai-calypso:generator-modal');
|
|
|
31
32
|
export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDetails, context, placement, }) => {
|
|
32
33
|
const { tracks } = useAnalytics();
|
|
33
34
|
const { recordEvent: recordTracksEvent } = tracks;
|
|
34
|
-
const { setSiteDetails, fetchAiAssistantFeature, loadLogoHistory } = useDispatch(STORE_NAME);
|
|
35
|
+
const { setSiteDetails, fetchAiAssistantFeature, loadLogoHistory, setIsLoadingHistory } = useDispatch(STORE_NAME);
|
|
35
36
|
const { getIsRequestingAiAssistantFeature } = select(STORE_NAME);
|
|
36
37
|
const [loadingState, setLoadingState] = useState(null);
|
|
37
38
|
const [initialPrompt, setInitialPrompt] = useState();
|
|
@@ -39,7 +40,7 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
39
40
|
const requestedFeatureData = useRef(false);
|
|
40
41
|
const [needsFeature, setNeedsFeature] = useState(false);
|
|
41
42
|
const [needsMoreRequests, setNeedsMoreRequests] = useState(false);
|
|
42
|
-
const { selectedLogo, getAiAssistantFeature, generateFirstPrompt, generateLogo, setContext } = useLogoGenerator();
|
|
43
|
+
const { selectedLogo, getAiAssistantFeature, generateFirstPrompt, generateLogo, setContext, tierPlansEnabled, } = useLogoGenerator();
|
|
43
44
|
const { featureFetchError, firstLogoPromptFetchError, clearErrors } = useRequestErrors();
|
|
44
45
|
const siteId = siteDetails?.ID;
|
|
45
46
|
const [logoAccepted, setLogoAccepted] = useState(false);
|
|
@@ -74,26 +75,30 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
74
75
|
const promptCreationCost = 1;
|
|
75
76
|
const currentLimit = feature?.currentTier?.value || 0;
|
|
76
77
|
const currentUsage = feature?.usagePeriod?.requestsCount || 0;
|
|
77
|
-
const isUnlimited = currentLimit === 1;
|
|
78
|
+
const isUnlimited = !tierPlansEnabled ? currentLimit > 0 : currentLimit === 1;
|
|
78
79
|
const hasNoNextTier = !feature?.nextTier; // If there is no next tier, the user cannot upgrade.
|
|
79
80
|
// The user needs an upgrade immediately if they have no logos and not enough requests remaining for one prompt and one logo generation.
|
|
80
81
|
const siteNeedsMoreRequests = !isUnlimited &&
|
|
81
82
|
!hasNoNextTier &&
|
|
82
83
|
!hasHistory &&
|
|
83
|
-
|
|
84
|
+
(tierPlansEnabled
|
|
85
|
+
? currentLimit - currentUsage < logoCost + promptCreationCost
|
|
86
|
+
: currentLimit < currentUsage);
|
|
84
87
|
// If the site requires an upgrade, show the upgrade screen immediately.
|
|
85
|
-
setNeedsFeature(
|
|
88
|
+
setNeedsFeature(currentLimit === 0);
|
|
86
89
|
setNeedsMoreRequests(siteNeedsMoreRequests);
|
|
87
|
-
if (
|
|
90
|
+
if (currentLimit === 0 || siteNeedsMoreRequests) {
|
|
88
91
|
setLoadingState(null);
|
|
89
92
|
return;
|
|
90
93
|
}
|
|
94
|
+
setIsLoadingHistory(true);
|
|
91
95
|
// Load the logo history and clear any deleted media.
|
|
92
96
|
await clearDeletedMedia(String(siteId));
|
|
93
97
|
loadLogoHistory(siteId);
|
|
94
98
|
// If there is any logo, we do not need to generate a first logo again.
|
|
95
99
|
if (!isLogoHistoryEmpty(String(siteId))) {
|
|
96
100
|
setLoadingState(null);
|
|
101
|
+
setIsLoadingHistory(false);
|
|
97
102
|
return;
|
|
98
103
|
}
|
|
99
104
|
// If the site does not require an upgrade and has no logos stored, generate the first prompt based on the site's data.
|
|
@@ -102,6 +107,7 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
102
107
|
catch (error) {
|
|
103
108
|
debug('Error fetching feature', error);
|
|
104
109
|
setLoadingState(null);
|
|
110
|
+
setIsLoadingHistory(false);
|
|
105
111
|
}
|
|
106
112
|
}, [
|
|
107
113
|
feature,
|
|
@@ -125,6 +131,7 @@ export const GeneratorModal = ({ isOpen, onClose, onApplyLogo, onReload, siteDet
|
|
|
125
131
|
setNeedsMoreRequests(false);
|
|
126
132
|
clearErrors();
|
|
127
133
|
setLogoAccepted(false);
|
|
134
|
+
setIsLoadingHistory(false);
|
|
128
135
|
recordTracksEvent(EVENT_MODAL_CLOSE, { context, placement });
|
|
129
136
|
};
|
|
130
137
|
const handleApplyLogo = (mediaId) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* External dependencies
|
|
4
4
|
*/
|
|
@@ -8,13 +8,14 @@ import clsx from 'clsx';
|
|
|
8
8
|
/**
|
|
9
9
|
* Internal dependencies
|
|
10
10
|
*/
|
|
11
|
+
import loader from '../assets/images/loader.gif';
|
|
11
12
|
import { EVENT_NAVIGATE } from '../constants.js';
|
|
12
13
|
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
13
14
|
import './history-carousel.scss';
|
|
14
15
|
export const HistoryCarousel = () => {
|
|
15
16
|
const { tracks } = useAnalytics();
|
|
16
17
|
const { recordEvent: recordTracksEvent } = tracks;
|
|
17
|
-
const { logos, selectedLogo, setSelectedLogoIndex, context } = useLogoGenerator();
|
|
18
|
+
const { logos, selectedLogo, setSelectedLogoIndex, context, isLoadingHistory } = useLogoGenerator();
|
|
18
19
|
const handleClick = (index) => {
|
|
19
20
|
recordTracksEvent(EVENT_NAVIGATE, {
|
|
20
21
|
context,
|
|
@@ -30,7 +31,7 @@ export const HistoryCarousel = () => {
|
|
|
30
31
|
}
|
|
31
32
|
return thumbnailURL.toString();
|
|
32
33
|
};
|
|
33
|
-
return (
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
return (_jsxs("div", { className: "jetpack-ai-logo-generator__carousel", children: [!logos.length && isLoadingHistory && (_jsx(Button, { disabled: true, className: clsx('jetpack-ai-logo-generator__carousel-logo'), children: _jsx("img", { height: "48", width: "48", src: loader, alt: 'loading' }) })), logos.map((logo, index) => (_jsx(Button, { className: clsx('jetpack-ai-logo-generator__carousel-logo', {
|
|
35
|
+
'is-selected': logo.url === selectedLogo.url,
|
|
36
|
+
}), onClick: () => handleClick(index), children: _jsx("img", { src: thumbnailFrom(logo.url), alt: logo.description }) }, logo.url)))] }));
|
|
36
37
|
};
|
|
@@ -74,6 +74,9 @@ const UseOnSiteButton = ({ onApplyLogo, }) => {
|
|
|
74
74
|
const LogoLoading = () => {
|
|
75
75
|
return (_jsxs(_Fragment, { children: [_jsx(ImageLoader, { className: "jetpack-ai-logo-generator-modal-presenter__logo" }), _jsx("span", { className: "jetpack-ai-logo-generator-modal-presenter__loading-text", children: __('Generating new logo…', 'jetpack-ai-client') })] }));
|
|
76
76
|
};
|
|
77
|
+
const LogoFetching = () => {
|
|
78
|
+
return (_jsxs(_Fragment, { children: [_jsx(ImageLoader, { className: "jetpack-ai-logo-generator-modal-presenter__logo" }), _jsx("span", { className: "jetpack-ai-logo-generator-modal-presenter__loading-text", children: __('Fetching previous logos…', 'jetpack-ai-client') })] }));
|
|
79
|
+
};
|
|
77
80
|
const LogoReady = ({ siteId, logo, onApplyLogo }) => {
|
|
78
81
|
return (_jsxs(_Fragment, { children: [_jsx("img", { src: logo.url, alt: logo.description, className: "jetpack-ai-logo-generator-modal-presenter__logo" }), _jsxs("div", { className: "jetpack-ai-logo-generator-modal-presenter__action-wrapper", children: [_jsx("span", { className: "jetpack-ai-logo-generator-modal-presenter__description", children: logo.description }), _jsxs("div", { className: "jetpack-ai-logo-generator-modal-presenter__actions", children: [_jsx(SaveInLibraryButton, { siteId: siteId }), _jsx(UseOnSiteButton, { onApplyLogo: onApplyLogo })] })] })] }));
|
|
79
82
|
};
|
|
@@ -81,13 +84,15 @@ const LogoUpdated = ({ logo }) => {
|
|
|
81
84
|
return (_jsxs(_Fragment, { children: [_jsx("img", { src: logo.url, alt: logo.description, className: "jetpack-ai-logo-generator-modal-presenter__logo" }), _jsxs("div", { className: "jetpack-ai-logo-generator-modal-presenter__success-wrapper", children: [_jsx(Icon, { icon: _jsx(CheckIcon, {}) }), _jsx("span", { children: __('Your new logo was set to the block!', 'jetpack-ai-client') })] })] }));
|
|
82
85
|
};
|
|
83
86
|
export const LogoPresenter = ({ logo = null, loading = false, onApplyLogo, logoAccepted = false, siteId, }) => {
|
|
87
|
+
// eslint-disable-next-line @wordpress/no-unused-vars-before-return -- @todo Start extending jetpack-js-tools/eslintrc/react in eslintrc, then we can remove this disable comment.
|
|
84
88
|
const { isRequestingImage } = useLogoGenerator();
|
|
85
89
|
const { saveToLibraryError, logoUpdateError } = useRequestErrors();
|
|
90
|
+
let logoContent;
|
|
86
91
|
if (!logo) {
|
|
87
|
-
|
|
92
|
+
debug('No logo provided, history still loading or logo being generated');
|
|
93
|
+
logoContent = _jsx(LogoFetching, {});
|
|
88
94
|
}
|
|
89
|
-
|
|
90
|
-
if (loading || isRequestingImage) {
|
|
95
|
+
else if (loading || isRequestingImage) {
|
|
91
96
|
logoContent = _jsx(LogoLoading, {});
|
|
92
97
|
}
|
|
93
98
|
else if (logoAccepted) {
|
|
@@ -16,6 +16,7 @@ import { EVENT_GENERATE, MINIMUM_PROMPT_LENGTH, EVENT_UPGRADE, EVENT_PLACEMENT_I
|
|
|
16
16
|
import { useCheckout } from '../hooks/use-checkout.js';
|
|
17
17
|
import useLogoGenerator from '../hooks/use-logo-generator.js';
|
|
18
18
|
import useRequestErrors from '../hooks/use-request-errors.js';
|
|
19
|
+
import { FairUsageNotice } from './fair-usage-notice.js';
|
|
19
20
|
import { UpgradeNudge } from './upgrade-nudge.js';
|
|
20
21
|
import './prompt.scss';
|
|
21
22
|
const debug = debugFactory('jetpack-ai-calypso:prompt-box');
|
|
@@ -27,7 +28,7 @@ export const Prompt = ({ initialPrompt = '' }) => {
|
|
|
27
28
|
const { enhancePromptFetchError, logoFetchError } = useRequestErrors();
|
|
28
29
|
const { nextTierCheckoutURL: checkoutUrl, hasNextTier } = useCheckout();
|
|
29
30
|
const hasPrompt = prompt?.length >= MINIMUM_PROMPT_LENGTH;
|
|
30
|
-
const { generateLogo, enhancePrompt, setIsEnhancingPrompt, isBusy, isEnhancingPrompt, site, getAiAssistantFeature, requireUpgrade, context, } = useLogoGenerator();
|
|
31
|
+
const { generateLogo, enhancePrompt, setIsEnhancingPrompt, isBusy, isEnhancingPrompt, site, getAiAssistantFeature, requireUpgrade, context, tierPlansEnabled, } = useLogoGenerator();
|
|
31
32
|
const enhancingLabel = __('Enhancing…', 'jetpack-ai-client');
|
|
32
33
|
const enhanceLabel = __('Enhance prompt', 'jetpack-ai-client');
|
|
33
34
|
const enhanceButtonLabel = isEnhancingPrompt ? enhancingLabel : enhanceLabel;
|
|
@@ -73,12 +74,12 @@ export const Prompt = ({ initialPrompt = '' }) => {
|
|
|
73
74
|
};
|
|
74
75
|
const onPromptPaste = (event) => {
|
|
75
76
|
event.preventDefault();
|
|
76
|
-
// Paste plain text only
|
|
77
|
-
const text = event.clipboardData.getData('text/plain');
|
|
78
77
|
const selection = window.getSelection();
|
|
79
78
|
if (!selection || !selection.rangeCount) {
|
|
80
79
|
return;
|
|
81
80
|
}
|
|
81
|
+
// Paste plain text only
|
|
82
|
+
const text = event.clipboardData.getData('text/plain');
|
|
82
83
|
selection.deleteFromDocument();
|
|
83
84
|
const range = selection.getRangeAt(0);
|
|
84
85
|
range.insertNode(document.createTextNode(text));
|
|
@@ -92,5 +93,5 @@ export const Prompt = ({ initialPrompt = '' }) => {
|
|
|
92
93
|
// The content editable div is expected to be updated by the enhance prompt, so warnings are suppressed
|
|
93
94
|
suppressContentEditableWarning: true, className: "prompt-query__input", onInput: onPromptInput, onPaste: onPromptPaste, "data-placeholder": __('Describe your site or simply ask for a logo specifying some details about it', 'jetpack-ai-client') }), _jsx(Button, { variant: "primary", className: "jetpack-ai-logo-generator__prompt-submit", onClick: onGenerate, disabled: isBusy || requireUpgrade || !hasPrompt, children: __('Generate', 'jetpack-ai-client') })] }), _jsxs("div", { className: "jetpack-ai-logo-generator__prompt-footer", children: [!isUnlimited && !requireUpgrade && (_jsxs("div", { className: "jetpack-ai-logo-generator__prompt-requests", children: [_jsx("div", { children: sprintf(
|
|
94
95
|
// translators: %u is the number of requests
|
|
95
|
-
__('%u requests remaining.', 'jetpack-ai-client'), requestsRemaining) }), hasNextTier && (_jsxs(_Fragment, { children: ["\u00A0", _jsx(Button, { variant: "link", href: checkoutUrl, target: "_blank", onClick: onUpgradeClick, children: __('Upgrade', 'jetpack-ai-client') })] })), "\u00A0", _jsx(Tooltip, { text: __('Logo generation costs 10 requests; prompt enhancement costs 1 request each', 'jetpack-ai-client'), placement: "bottom", children: _jsx(Icon, { className: "prompt-footer__icon", icon: info }) })] })),
|
|
96
|
+
__('%u requests remaining.', 'jetpack-ai-client'), requestsRemaining) }), hasNextTier && (_jsxs(_Fragment, { children: ["\u00A0", _jsx(Button, { variant: "link", href: checkoutUrl, target: "_blank", onClick: onUpgradeClick, children: __('Upgrade', 'jetpack-ai-client') })] })), "\u00A0", _jsx(Tooltip, { text: __('Logo generation costs 10 requests; prompt enhancement costs 1 request each', 'jetpack-ai-client'), placement: "bottom", children: _jsx(Icon, { className: "prompt-footer__icon", icon: info }) })] })), requireUpgrade && tierPlansEnabled && _jsx(UpgradeNudge, {}), requireUpgrade && !tierPlansEnabled && _jsx(FairUsageNotice, {}), enhancePromptFetchError && (_jsx("div", { className: "jetpack-ai-logo-generator__prompt-error", children: __('Error enhancing prompt. Please try again.', 'jetpack-ai-client') })), logoFetchError && (_jsx("div", { className: "jetpack-ai-logo-generator__prompt-error", children: __('Error generating logo. Please try again.', 'jetpack-ai-client') }))] })] }));
|
|
96
97
|
};
|
|
@@ -10,10 +10,11 @@ import debugFactory from 'debug';
|
|
|
10
10
|
import { STORE_NAME } from '../store/index.js';
|
|
11
11
|
const debug = debugFactory('ai-client:logo-generator:use-checkout');
|
|
12
12
|
export const useCheckout = () => {
|
|
13
|
-
const { nextTier } = useSelect(select => {
|
|
13
|
+
const { nextTier, tierPlansEnabled } = useSelect(select => {
|
|
14
14
|
const selectors = select(STORE_NAME);
|
|
15
15
|
return {
|
|
16
16
|
nextTier: selectors.getAiAssistantFeature().nextTier,
|
|
17
|
+
tierPlansEnabled: selectors.getAiAssistantFeature().tierPlansEnabled,
|
|
17
18
|
};
|
|
18
19
|
}, []);
|
|
19
20
|
/**
|
|
@@ -22,7 +23,7 @@ export const useCheckout = () => {
|
|
|
22
23
|
const wpcomCheckoutUrl = new URL(`https://jetpack.com/redirect/`);
|
|
23
24
|
wpcomCheckoutUrl.searchParams.set('source', 'jetpack-ai-yearly-tier-upgrade-nudge');
|
|
24
25
|
wpcomCheckoutUrl.searchParams.set('site', getSiteFragment());
|
|
25
|
-
wpcomCheckoutUrl.searchParams.set('path', `jetpack_ai_yearly:-q-${nextTier?.limit}`);
|
|
26
|
+
wpcomCheckoutUrl.searchParams.set('path', tierPlansEnabled ? `jetpack_ai_yearly:-q-${nextTier?.limit}` : 'jetpack_ai_yearly');
|
|
26
27
|
/**
|
|
27
28
|
* Open the product interstitial page
|
|
28
29
|
*/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useSelect } from '@wordpress/data';
|
|
3
|
+
import { createInterpolateElement } from '@wordpress/element';
|
|
4
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
5
|
+
/**
|
|
6
|
+
* Internal dependencies
|
|
7
|
+
*/
|
|
8
|
+
import { STORE_NAME } from '../store/index.js';
|
|
9
|
+
const useFairUsageNoticeMessage = () => {
|
|
10
|
+
const { usagePeriod } = useSelect(select => {
|
|
11
|
+
const selectors = select(STORE_NAME);
|
|
12
|
+
return {
|
|
13
|
+
usagePeriod: selectors.getAiAssistantFeature().nextTier,
|
|
14
|
+
};
|
|
15
|
+
}, []);
|
|
16
|
+
const getFormattedUsagePeriodStartDate = planUsagePeriod => {
|
|
17
|
+
if (!planUsagePeriod?.nextStart) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const nextUsagePeriodStartDate = new Date(planUsagePeriod.nextStart);
|
|
21
|
+
return (nextUsagePeriodStartDate.toLocaleString('default', { month: 'long' }) +
|
|
22
|
+
' ' +
|
|
23
|
+
nextUsagePeriodStartDate.getDate());
|
|
24
|
+
};
|
|
25
|
+
const getFairUsageNoticeMessage = resetDateString => {
|
|
26
|
+
const fairUsageMessage = __("You've reached this month's request limit, per our <link>fair usage policy</link>.", 'jetpack-ai-client');
|
|
27
|
+
if (!resetDateString) {
|
|
28
|
+
return fairUsageMessage;
|
|
29
|
+
}
|
|
30
|
+
// Translators: %s is the date when the requests will reset.
|
|
31
|
+
const dateMessage = __('Requests will reset on %s.', 'jetpack-ai-client');
|
|
32
|
+
const formattedDateMessage = sprintf(dateMessage, resetDateString);
|
|
33
|
+
return `${fairUsageMessage} ${formattedDateMessage}`;
|
|
34
|
+
};
|
|
35
|
+
const nextUsagePeriodStartDateString = getFormattedUsagePeriodStartDate(usagePeriod);
|
|
36
|
+
// Get the proper template based on the presence of the next usage period start date.
|
|
37
|
+
const fairUsageNoticeMessage = getFairUsageNoticeMessage(nextUsagePeriodStartDateString);
|
|
38
|
+
const fairUsageNoticeMessageElement = createInterpolateElement(fairUsageNoticeMessage, {
|
|
39
|
+
link: (_jsx("a", { href: "https://jetpack.com/redirect/?source=ai-logo-generator-fair-usage-policy", target: "_blank", rel: "noreferrer" })),
|
|
40
|
+
});
|
|
41
|
+
return fairUsageNoticeMessageElement;
|
|
42
|
+
};
|
|
43
|
+
export default useFairUsageNoticeMessage;
|
|
@@ -42,5 +42,8 @@ declare const useLogoGenerator: () => {
|
|
|
42
42
|
getAiAssistantFeature: (siteId?: string) => Partial<import("../store/types.js").AiFeatureProps>;
|
|
43
43
|
requireUpgrade: boolean;
|
|
44
44
|
context: string;
|
|
45
|
+
tierPlansEnabled: boolean;
|
|
46
|
+
isLoadingHistory: boolean;
|
|
47
|
+
setIsLoadingHistory: any;
|
|
45
48
|
};
|
|
46
49
|
export default useLogoGenerator;
|
|
@@ -16,8 +16,8 @@ import { STORE_NAME } from '../store/index.js';
|
|
|
16
16
|
import useRequestErrors from './use-request-errors.js';
|
|
17
17
|
const debug = debugFactory('jetpack-ai-calypso:use-logo-generator');
|
|
18
18
|
const useLogoGenerator = () => {
|
|
19
|
-
const { setSelectedLogoIndex, setIsSavingLogoToLibrary, setIsApplyingLogo, setIsRequestingImage, setIsEnhancingPrompt, increaseAiAssistantRequestsCount, addLogoToHistory, setContext, } = useDispatch(STORE_NAME);
|
|
20
|
-
const { logos, selectedLogoIndex, selectedLogo, siteDetails, isSavingLogoToLibrary, isApplyingLogo, isEnhancingPrompt, isBusy, isRequestingImage, getAiAssistantFeature, requireUpgrade, context, } = useSelect(select => {
|
|
19
|
+
const { setSelectedLogoIndex, setIsSavingLogoToLibrary, setIsApplyingLogo, setIsRequestingImage, setIsEnhancingPrompt, increaseAiAssistantRequestsCount, addLogoToHistory, setContext, setIsLoadingHistory, } = useDispatch(STORE_NAME);
|
|
20
|
+
const { logos, selectedLogoIndex, selectedLogo, siteDetails, isSavingLogoToLibrary, isApplyingLogo, isEnhancingPrompt, isBusy, isRequestingImage, getAiAssistantFeature, requireUpgrade, context, tierPlansEnabled, isLoadingHistory, } = useSelect(select => {
|
|
21
21
|
const selectors = select(STORE_NAME);
|
|
22
22
|
return {
|
|
23
23
|
logos: selectors.getLogos(),
|
|
@@ -32,6 +32,8 @@ const useLogoGenerator = () => {
|
|
|
32
32
|
getAiAssistantFeature: selectors.getAiAssistantFeature,
|
|
33
33
|
requireUpgrade: selectors.getRequireUpgrade(),
|
|
34
34
|
context: selectors.getContext(),
|
|
35
|
+
tierPlansEnabled: selectors.getTierPlansEnabled(),
|
|
36
|
+
isLoadingHistory: selectors.getIsLoadingHistory(),
|
|
35
37
|
};
|
|
36
38
|
}, []);
|
|
37
39
|
const { setFirstLogoPromptFetchError, setEnhancePromptFetchError, setLogoFetchError, setSaveToLibraryError, setLogoUpdateError, } = useRequestErrors();
|
|
@@ -281,6 +283,9 @@ User request:${prompt}`;
|
|
|
281
283
|
getAiAssistantFeature,
|
|
282
284
|
requireUpgrade,
|
|
283
285
|
context,
|
|
286
|
+
tierPlansEnabled,
|
|
287
|
+
isLoadingHistory,
|
|
288
|
+
setIsLoadingHistory,
|
|
284
289
|
};
|
|
285
290
|
};
|
|
286
291
|
export default useLogoGenerator;
|
|
@@ -6,7 +6,7 @@ import wpcomLimitedRequest from '../lib/wpcom-limited-request.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* Types & Constants
|
|
8
8
|
*/
|
|
9
|
-
import { ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT, ACTION_REQUEST_AI_ASSISTANT_FEATURE, ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE, ACTION_SET_SITE_DETAILS, ACTION_STORE_AI_ASSISTANT_FEATURE, ACTION_SET_TIER_PLANS_ENABLED, ACTION_SET_SELECTED_LOGO_INDEX, ACTION_ADD_LOGO_TO_HISTORY, ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY, ACTION_SAVE_SELECTED_LOGO, ACTION_SET_IS_REQUESTING_IMAGE, ACTION_SET_IS_APPLYING_LOGO, ACTION_SET_IS_ENHANCING_PROMPT, ACTION_SET_SITE_HISTORY, ACTION_SET_FEATURE_FETCH_ERROR, ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR, ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR, ACTION_SET_LOGO_FETCH_ERROR, ACTION_SET_LOGO_UPDATE_ERROR, ACTION_SET_SAVE_TO_LIBRARY_ERROR, ACTION_SET_CONTEXT, } from './constants.js';
|
|
9
|
+
import { ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT, ACTION_REQUEST_AI_ASSISTANT_FEATURE, ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE, ACTION_SET_SITE_DETAILS, ACTION_STORE_AI_ASSISTANT_FEATURE, ACTION_SET_TIER_PLANS_ENABLED, ACTION_SET_SELECTED_LOGO_INDEX, ACTION_ADD_LOGO_TO_HISTORY, ACTION_SET_IS_SAVING_LOGO_TO_LIBRARY, ACTION_SAVE_SELECTED_LOGO, ACTION_SET_IS_REQUESTING_IMAGE, ACTION_SET_IS_APPLYING_LOGO, ACTION_SET_IS_ENHANCING_PROMPT, ACTION_SET_SITE_HISTORY, ACTION_SET_FEATURE_FETCH_ERROR, ACTION_SET_FIRST_LOGO_PROMPT_FETCH_ERROR, ACTION_SET_ENHANCE_PROMPT_FETCH_ERROR, ACTION_SET_LOGO_FETCH_ERROR, ACTION_SET_LOGO_UPDATE_ERROR, ACTION_SET_SAVE_TO_LIBRARY_ERROR, ACTION_SET_CONTEXT, ACTION_SET_IS_LOADING_HISTORY, } from './constants.js';
|
|
10
10
|
/**
|
|
11
11
|
* Map the response from the `sites/$site/ai-assistant-feature`
|
|
12
12
|
* endpoint to the AI Assistant feature props.
|
|
@@ -32,6 +32,7 @@ export function mapAiFeatureResponseToAiFeatureProps(response) {
|
|
|
32
32
|
nextTier: response['next-tier'],
|
|
33
33
|
tierPlansEnabled: !!response['tier-plans-enabled'],
|
|
34
34
|
costs: response.costs,
|
|
35
|
+
featuresControl: response['features-control'],
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
const actions = {
|
|
@@ -189,5 +190,11 @@ const actions = {
|
|
|
189
190
|
context,
|
|
190
191
|
};
|
|
191
192
|
},
|
|
193
|
+
setIsLoadingHistory(isLoadingHistory) {
|
|
194
|
+
return {
|
|
195
|
+
type: ACTION_SET_IS_LOADING_HISTORY,
|
|
196
|
+
isLoadingHistory,
|
|
197
|
+
};
|
|
198
|
+
},
|
|
192
199
|
};
|
|
193
200
|
export default actions;
|
|
@@ -33,6 +33,7 @@ export declare const ACTION_SAVE_SELECTED_LOGO = "SAVE_SELECTED_LOGO";
|
|
|
33
33
|
export declare const ACTION_SET_IS_REQUESTING_IMAGE = "SET_IS_REQUESTING_IMAGE";
|
|
34
34
|
export declare const ACTION_SET_IS_ENHANCING_PROMPT = "SET_IS_ENHANCING_PROMPT";
|
|
35
35
|
export declare const ACTION_SET_SITE_HISTORY = "SET_SITE_HISTORY";
|
|
36
|
+
export declare const ACTION_SET_IS_LOADING_HISTORY = "SET_IS_LOADING_HISTORY";
|
|
36
37
|
/**
|
|
37
38
|
* Logo generator error actions
|
|
38
39
|
*/
|
|
@@ -33,6 +33,7 @@ export const ACTION_SAVE_SELECTED_LOGO = 'SAVE_SELECTED_LOGO';
|
|
|
33
33
|
export const ACTION_SET_IS_REQUESTING_IMAGE = 'SET_IS_REQUESTING_IMAGE';
|
|
34
34
|
export const ACTION_SET_IS_ENHANCING_PROMPT = 'SET_IS_ENHANCING_PROMPT';
|
|
35
35
|
export const ACTION_SET_SITE_HISTORY = 'SET_SITE_HISTORY';
|
|
36
|
+
export const ACTION_SET_IS_LOADING_HISTORY = 'SET_IS_LOADING_HISTORY';
|
|
36
37
|
/**
|
|
37
38
|
* Logo generator error actions
|
|
38
39
|
*/
|