My subscription product updates (#40752)

* Marketplace: Populate the table rows with components

* Marketplace: add links to dropdown menu and fix fallback product icons

* Marketplace: fix popover width

* Add My subscriptions install (#40630)

* Marketplace: add plugin install from the subscriptions page

Co-authored-by: berislav grgičak <berislav.grgicak@gmail.com>

* Marketplace: Use the activation function to show install button

---------

Co-authored-by: raicem <unalancem@gmail.com>

* Add WP updates script to the extensions page

* Add update button

* Add update data to subscriptions

* Update plugins

* Prevent update if license unavailable

* Add changefile(s) from automation for the following project(s): woocommerce

* Remove all data from API

* Linter fixes

* Linter fixes

* Remove merge string

* Update link style

* Add comment for updates.js

* Prevent updates if required data is missing

* Return removed slug code

* My subscriptions action modals (#40934)

* Add renew modal

* Update install to run until new data loaded

* Add activate modal

* Add connect modal

* Add renew button

* Renewal button

* Rename activate to connect

* Add subscribe button

* Add action buttons

* Remove unused const

* Add changefile(s) from automation for the following project(s): woocommerce

* Update plugins/woocommerce-admin/client/marketplace/components/my-subscriptions/my-subscriptions.scss

Co-authored-by: And Finally <andfinally@users.noreply.github.com>

* Update import path

---------

Co-authored-by: And Finally <andfinally@users.noreply.github.com>
Co-authored-by: github-actions <github-actions@github.com>

---------

Co-authored-by: raicem <unalancem@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: And Finally <andfinally@users.noreply.github.com>
This commit is contained in:
berislav grgičak 2023-10-27 06:08:27 +02:00 committed by GitHub
parent 83b7b95850
commit 53c4fe6afb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 656 additions and 101 deletions

View File

@ -7,3 +7,4 @@ export const MARKETPLACE_CATEGORY_API_PATH =
'/wp-json/wccom-extensions/1.0/categories';
export const MARKETPLACE_ITEMS_PER_PAGE = 60;
export const MARKETPLACE_SEARCH_RESULTS_PER_PAGE = 8;
export const MARKETPLACE_CART_PATH = MARKETPLACE_HOST + '/cart/';

View File

@ -102,6 +102,19 @@
margin-bottom: 0;
}
.woocommerce-marketplace__my-subscriptions-version {
padding: 6px 12px;
}
.woocommerce-marketplace__my-subscriptions__table__header--version > span {
display: inline-block;
padding: 0 12px;
}
.woocommerce-marketplace__my-subscriptions__table .components-button.is-link {
text-decoration: none;
padding: 6px 12px;
}
.woocommerce-marketplace__my-subscriptions--connect {
display: flex;
flex-direction: column;
@ -131,3 +144,16 @@
margin-bottom: $grid-unit-30;
}
}
.woocommerce-marketplace__my-subscriptions__table__header--actions {
text-align: right;
}
.woocommerce-marketplace__my-subscriptions__actions {
display: flex;
justify-content: end;
.components-button {
margin-right: $grid-unit-10;
}
}

View File

@ -0,0 +1,81 @@
/**
* External dependencies
*/
import { Button, Icon } from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { useContext, useState } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { SubscriptionsContext } from '~/marketplace/contexts/subscriptions-context';
import { connectProduct } from '~/marketplace/utils/functions';
import { Subscription } from '../../types';
interface ConnectProps {
subscription: Subscription;
onClose?: () => void;
variant?: Button.ButtonVariant;
}
export default function ConnectButton( props: ConnectProps ) {
const [ isConnecting, setIsConnecting ] = useState( false );
const { createWarningNotice, createSuccessNotice } =
useDispatch( 'core/notices' );
const { loadSubscriptions } = useContext( SubscriptionsContext );
const connect = () => {
setIsConnecting( true );
connectProduct( props.subscription.product_key )
.then( () => {
loadSubscriptions( false ).then( () => {
createSuccessNotice(
sprintf(
// translators: %s is the product name.
__( '%s successfully connected.', 'woocommerce' ),
props.subscription.product_name
),
{
icon: <Icon icon="yes" />,
}
);
setIsConnecting( false );
if ( props.onClose ) {
props.onClose();
}
} );
} )
.catch( () => {
createWarningNotice(
sprintf(
// translators: %s is the product name.
__( '%s couldnt be connected.', 'woocommerce' ),
props.subscription.product_name
),
{
actions: [
{
label: __( 'Try again', 'woocommerce' ),
onClick: connect,
},
],
}
);
setIsConnecting( false );
if ( props.onClose ) {
props.onClose();
}
} );
};
return (
<Button
onClick={ connect }
variant={ props.variant ?? 'secondary' }
isBusy={ isConnecting }
disabled={ isConnecting }
>
{ __( 'Connect', 'woocommerce' ) }
</Button>
);
}

View File

@ -0,0 +1,54 @@
/**
* External dependencies
*/
import { Button, ButtonGroup, Modal } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { Subscription } from '../../types';
import ConnectButton from './connect-button';
interface ConnectProps {
subscription: Subscription;
onClose: () => void;
}
export default function ConnectModal( props: ConnectProps ) {
return (
<Modal
title={ __( 'Connect to update', 'woocommerce' ) }
onRequestClose={ props.onClose }
focusOnMount={ true }
className="woocommerce-marketplace__header-account-modal"
style={ { borderRadius: 4 } }
overlayClassName="woocommerce-marketplace__header-account-modal-overlay"
>
<p className="woocommerce-marketplace__header-account-modal-text">
{ sprintf(
// translators: %s is the product version number (e.g. 1.0.2).
__(
'Version %s is available. To enable this update you need to connect your subscription to this store.',
'woocommerce'
),
props.subscription.version
) }
</p>
<ButtonGroup className="woocommerce-marketplace__header-account-modal-button-group">
<Button
variant="tertiary"
onClick={ props.onClose }
className="woocommerce-marketplace__header-account-modal-button"
>
{ __( 'Cancel', 'woocommerce' ) }
</Button>
<ConnectButton
subscription={ props.subscription }
onClose={ props.onClose }
variant="primary"
/>
</ButtonGroup>
</Modal>
);
}

View File

@ -1,23 +1,23 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { Button, Icon } from '@wordpress/components';
import { useContext, useState } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { useContext, useState } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { Subscription } from '../../types';
import { installProduct } from '../../../../utils/functions';
import { SubscriptionsContext } from '../../../../contexts/subscriptions-context';
import { installProduct } from '../../../../utils/functions';
import { Subscription } from '../../types';
interface ActivationToggleProps {
interface InstallProps {
subscription: Subscription;
}
export default function Install( props: ActivationToggleProps ) {
export default function Install( props: InstallProps ) {
const [ loading, setLoading ] = useState( false );
const { createWarningNotice, createSuccessNotice } =
useDispatch( 'core/notices' );
@ -27,17 +27,19 @@ export default function Install( props: ActivationToggleProps ) {
setLoading( true );
installProduct( props.subscription.product_key )
.then( () => {
loadSubscriptions( false );
createSuccessNotice(
sprintf(
// translators: %s is the product name.
__( '%s successfully installed.', 'woocommerce' ),
props.subscription.product_name
),
{
icon: <Icon icon="yes" />,
}
);
loadSubscriptions( false ).then( () => {
createSuccessNotice(
sprintf(
// translators: %s is the product name.
__( '%s successfully installed.', 'woocommerce' ),
props.subscription.product_name
),
{
icon: <Icon icon="yes" />,
}
);
setLoading( false );
} );
} )
.catch( () => {
createWarningNotice(
@ -55,8 +57,6 @@ export default function Install( props: ActivationToggleProps ) {
],
}
);
} )
.finally( () => {
setLoading( false );
} );
};
@ -71,7 +71,7 @@ export default function Install( props: ActivationToggleProps ) {
return (
<Button
variant="primary"
variant="link"
isBusy={ loading }
disabled={ loading }
onClick={ install }

View File

@ -0,0 +1,31 @@
/**
* External dependencies
*/
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { MARKETPLACE_CART_PATH } from '~/marketplace/components/constants';
import { appendURLParams } from '~/marketplace/utils/functions';
import { Subscription } from '../../types';
interface RenewProps {
subscription: Subscription;
variant?: Button.ButtonVariant;
}
export default function RenewButton( props: RenewProps ) {
const renewUrl = appendURLParams( MARKETPLACE_CART_PATH, [
[ 'renew_product', props.subscription.product_id.toString() ],
[ 'product_key', props.subscription.product_key ],
[ 'order_id', props.subscription.order_id.toString() ],
] );
return (
<Button href={ renewUrl } variant={ props.variant ?? 'secondary' }>
{ __( 'Renew', 'woocommerce' ) }
</Button>
);
}

View File

@ -0,0 +1,53 @@
/**
* External dependencies
*/
import { Button, ButtonGroup, Modal } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { Subscription } from '../../types';
import RenewButton from './renew-button';
interface RenewProps {
subscription: Subscription;
onClose: () => void;
}
export default function RenewModal( props: RenewProps ) {
return (
<Modal
title={ __( 'Renew to update', 'woocommerce' ) }
onRequestClose={ props.onClose }
focusOnMount={ true }
className="woocommerce-marketplace__header-account-modal"
style={ { borderRadius: 4 } }
overlayClassName="woocommerce-marketplace__header-account-modal-overlay"
>
<p className="woocommerce-marketplace__header-account-modal-text">
{ sprintf(
// translators: %s is the product version number (e.g. 1.0.2).
__(
'Version %s is available. To enable this update you need to renew your subscription.',
'woocommerce'
),
props.subscription.version
) }
</p>
<ButtonGroup className="woocommerce-marketplace__header-account-modal-button-group">
<Button
variant="tertiary"
onClick={ props.onClose }
className="woocommerce-marketplace__header-account-modal-button"
>
{ __( 'Cancel', 'woocommerce' ) }
</Button>
<RenewButton
subscription={ props.subscription }
variant="primary"
/>
</ButtonGroup>
</Modal>
);
}

View File

@ -0,0 +1,28 @@
/**
* External dependencies
*/
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { MARKETPLACE_CART_PATH } from '../../../constants';
import { appendURLParams } from '../../../../utils/functions';
import { Subscription } from '../../types';
interface SubscribeProps {
subscription: Subscription;
variant?: Button.ButtonVariant;
}
export default function SubscribeButton( props: SubscribeProps ) {
const subscribeUrl = appendURLParams( MARKETPLACE_CART_PATH, [
[ 'add-to-cart', props.subscription.product_id.toString() ],
] );
return (
<Button href={ subscribeUrl } variant={ props.variant ?? 'secondary' }>
{ __( 'Subscribe', 'woocommerce' ) }
</Button>
);
}

View File

@ -0,0 +1,53 @@
/**
* External dependencies
*/
import { Button, ButtonGroup, Modal } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { Subscription } from '../../types';
import SubscribeButton from './subscribe-button';
interface SubscribeProps {
subscription: Subscription;
onClose: () => void;
}
export default function SubscribeModal( props: SubscribeProps ) {
return (
<Modal
title={ __( 'Subscribe to update', 'woocommerce' ) }
onRequestClose={ props.onClose }
focusOnMount={ true }
className="woocommerce-marketplace__header-account-modal"
style={ { borderRadius: 4 } }
overlayClassName="woocommerce-marketplace__header-account-modal-overlay"
>
<p className="woocommerce-marketplace__header-account-modal-text">
{ sprintf(
// translators: %s is the product version number (e.g. 1.0.2).
__(
'Version %s is available. To enable this update you need to purchase a subscription.',
'woocommerce'
),
props.subscription.version
) }
</p>
<ButtonGroup className="woocommerce-marketplace__header-account-modal-button-group">
<Button
variant="tertiary"
onClick={ props.onClose }
className="woocommerce-marketplace__header-account-modal-button"
>
{ __( 'Cancel', 'woocommerce' ) }
</Button>
<SubscribeButton
subscription={ props.subscription }
variant="primary"
/>
</ButtonGroup>
</Modal>
);
}

View File

@ -0,0 +1,165 @@
/**
* External dependencies
*/
import { Button, Icon } from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { useContext, useState } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { SubscriptionsContext } from '../../../../contexts/subscriptions-context';
import { Subscription } from '../../types';
import ConnectModal from './connect-modal';
import RenewModal from './renew-modal';
import SubscribeModal from './subscribe-modal';
interface UpdateProps {
subscription: Subscription;
}
export default function Update( props: UpdateProps ) {
const [ showModal, setShowModal ] = useState( false );
const [ isUpdating, setIsUpdating ] = useState( false );
const { createWarningNotice, createSuccessNotice } =
useDispatch( 'core/notices' );
const { loadSubscriptions } = useContext( SubscriptionsContext );
const canUpdate =
props.subscription.active &&
props.subscription.local &&
props.subscription.local.slug &&
props.subscription.local.path;
function update() {
if ( ! canUpdate ) {
setShowModal( true );
return;
}
if ( ! window.wp.updates ) {
createWarningNotice(
sprintf(
// translators: %s is the product name.
__( '%s couldnt be updated.', 'woocommerce' ),
props.subscription.product_name
),
{
actions: [
{
label: __(
'Reload page and try again',
'woocommerce'
),
onClick: () => {
window.location.reload();
},
},
],
}
);
return;
}
setIsUpdating( true );
const action =
props.subscription.local.type === 'plugin'
? 'update-plugin'
: 'update-theme';
window.wp.updates
.ajax( action, {
slug: props.subscription.local.slug,
plugin: props.subscription.local.path,
theme: props.subscription.local.path,
} )
.then( () => {
loadSubscriptions( false );
createSuccessNotice(
sprintf(
// translators: %s is the product name.
__( '%s updated successfully.', 'woocommerce' ),
props.subscription.product_name
),
{
icon: <Icon icon="yes" />,
}
);
} )
.catch( () => {
createWarningNotice(
sprintf(
// translators: %s is the product name.
__( '%s couldnt be updated.', 'woocommerce' ),
props.subscription.product_name
),
{
actions: [
{
label: __( 'Try again', 'woocommerce' ),
onClick: update,
},
],
}
);
} )
.always( () => {
setIsUpdating( false );
} );
}
const modal = () => {
if ( ! showModal ) {
return null;
}
if ( props.subscription.product_key === '' ) {
return (
<SubscribeModal
onClose={ () => setShowModal( false ) }
subscription={ props.subscription }
/>
);
} else if ( props.subscription.expired ) {
return (
<RenewModal
subscription={ props.subscription }
onClose={ () => setShowModal( false ) }
/>
);
} else if ( ! props.subscription.active ) {
return (
<ConnectModal
subscription={ props.subscription }
onClose={ () => setShowModal( false ) }
/>
);
}
return null;
};
return (
<>
{ modal() }
<Button
variant="link"
className="woocommerce-marketplace__my-subscriptions-update"
onClick={ update }
isBusy={ isUpdating }
disabled={ isUpdating }
label={ sprintf(
// translators: %s is the product version.
__( 'Update to %s', 'woocommerce' ),
props.subscription.version
) }
showTooltip={ true }
tooltipPosition="top center"
>
{ isUpdating
? __( 'Updating', 'woocommerce' )
: __( 'Update', 'woocommerce' ) }
</Button>
</>
);
}

View File

@ -1,25 +0,0 @@
/**
* External dependencies
*/
import { ToggleControl } from '@wordpress/components';
/**
* Internal dependencies
*/
import { Subscription } from '../../types';
import Install from './install';
interface ActivationToggleProps {
subscription: Subscription;
}
export default function ActivationToggle( props: ActivationToggleProps ) {
if (
props.subscription.local.installed === false &&
props.subscription.maxed === false
) {
return <Install subscription={ props.subscription } />;
}
return <ToggleControl checked={ props.subscription.active } />;
}

View File

@ -1,18 +1,23 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { TableRow } from '@woocommerce/components/build-types/table/types';
import { Icon, plugins } from '@wordpress/icons';
import { gmdateI18n } from '@wordpress/date';
import { __, sprintf } from '@wordpress/i18n';
import { Icon, plugins } from '@wordpress/icons';
/**
* Internal dependencies
*/
import { Subscription } from '../../types';
import StatusPopover from './status-popover';
import ActivationToggle from './activation-toggle';
import ConnectButton from '../actions/connect-button';
import Install from '../actions/install';
import RenewButton from '../actions/renew-button';
import SubscribeButton from '../actions/subscribe-button';
import Update from '../actions/update';
import ActionsDropdownMenu from './actions-dropdown-menu';
import StatusPopover from './status-popover';
import Version from './version';
// TODO: Add explanations.
function getStatus( subscription: Subscription ): {
@ -56,21 +61,21 @@ function getStatus( subscription: Subscription ): {
};
}
function getVersion( subscription: Subscription ): string {
function getVersion( subscription: Subscription ): string | JSX.Element {
if ( subscription.local.version === subscription.version ) {
return subscription.local.version;
return <Version span={ subscription.local.version } />;
}
if ( subscription.local.version && subscription.version ) {
return subscription.local.version + ' > ' + subscription.version;
return <Update subscription={ subscription } />;
}
if ( subscription.version ) {
return subscription.version;
return <Version span={ subscription.version } />;
}
if ( subscription.local.version ) {
return subscription.local.version;
return <Version span={ subscription.local.version } />;
}
return '';
@ -175,16 +180,26 @@ export function version( subscription: Subscription ): TableRow {
};
}
export function activation( subscription: Subscription ): TableRow {
const displayElement = <ActivationToggle subscription={ subscription } />;
export function actions( subscription: Subscription ): TableRow {
let actionButton = null;
if ( subscription.product_key === '' ) {
actionButton = <SubscribeButton subscription={ subscription } />;
} else if ( subscription.expired ) {
actionButton = <RenewButton subscription={ subscription } />;
} else if ( subscription.local.installed === false ) {
actionButton = <Install subscription={ subscription } />;
} else if ( subscription.active === false ) {
actionButton = (
<ConnectButton subscription={ subscription } variant="link" />
);
}
return {
display: displayElement,
};
}
export function actions(): TableRow {
return {
display: <ActionsDropdownMenu />,
display: (
<div className="woocommerce-marketplace__my-subscriptions__actions">
{ actionButton }
<ActionsDropdownMenu />
</div>
),
};
}

View File

@ -0,0 +1,11 @@
interface VersionProps {
span: string;
}
export default function Version( props: VersionProps ) {
return (
<span className="woocommerce-marketplace__my-subscriptions-version">
{ props.span }
</span>
);
}

View File

@ -7,13 +7,12 @@ import { TableRow } from '@woocommerce/components/build-types/table/types';
*/
import { Subscription } from '../types';
import {
actions,
autoRenew,
expiry,
productName,
status,
expiry,
autoRenew,
version,
activation,
actions,
} from './rows/functions';
export function availableSubscriptionRow( item: Subscription ): TableRow[] {
@ -23,8 +22,7 @@ export function availableSubscriptionRow( item: Subscription ): TableRow[] {
expiry( item ),
autoRenew( item ),
version( item ),
activation( item ),
actions(),
actions( item ),
];
}
@ -35,7 +33,6 @@ export function installedSubscriptionRow( item: Subscription ): TableRow[] {
expiry( item ),
autoRenew( item ),
version( item ),
activation( item ),
actions(),
actions( item ),
];
}

View File

@ -1,12 +1,12 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { Table, TablePlaceholder } from '@woocommerce/components';
import {
TableHeader,
TableRow,
} from '@woocommerce/components/build-types/table/types';
import { __ } from '@wordpress/i18n';
const tableHeadersDefault = [
{
@ -45,10 +45,19 @@ function SubscriptionsTable( props: {
);
}
const headersWithClasses = props.headers.map( ( header ) => {
return {
...header,
cellClassName:
'woocommerce-marketplace__my-subscriptions__table__header--' +
header.key,
};
} );
return (
<Table
className="woocommerce-marketplace__my-subscriptions__table"
headers={ props.headers }
headers={ headersWithClasses }
rows={ props.rows }
/>
);
@ -60,10 +69,6 @@ export function InstalledSubscriptionsTable( props: {
} ) {
const headers = [
...tableHeadersDefault,
{
key: 'activated',
label: __( 'Activated', 'woocommerce' ),
},
{
key: 'actions',
label: __( 'Actions', 'woocommerce' ),
@ -85,10 +90,6 @@ export function AvailableSubscriptionsTable( props: {
} ) {
const headers = [
...tableHeadersDefault,
{
key: 'install',
label: __( 'Install', 'woocommerce' ),
},
{
key: 'actions',
label: __( 'Actions', 'woocommerce' ),

View File

@ -4,6 +4,7 @@ export type Subscription = {
product_name: string;
product_url: string;
product_icon: string;
zip_slug: string;
key_type: string;
key_type_label: string;
autorenew: boolean;
@ -30,6 +31,9 @@ export interface SubscriptionLocal {
installed: boolean;
active: boolean;
version: string;
type: string;
slug: string;
path: string;
}
export interface SubscriptionShare {

View File

@ -13,7 +13,7 @@ import { fetchSubscriptions } from '../utils/functions';
export const SubscriptionsContext = createContext< SubscriptionsContextType >( {
subscriptions: [],
setSubscriptions: () => {},
loadSubscriptions: () => {},
loadSubscriptions: () => new Promise( () => {} ),
isLoading: true,
setIsLoading: () => {},
} );
@ -31,7 +31,7 @@ export function SubscriptionsContextProvider( props: {
setIsLoading( true );
}
fetchSubscriptions()
return fetchSubscriptions()
.then( ( subscriptionResponse ) => {
setSubscriptions( subscriptionResponse );
} )
@ -42,12 +42,14 @@ export function SubscriptionsContextProvider( props: {
} );
};
useEffect( () => loadSubscriptions( true ), [] );
useEffect( () => {
loadSubscriptions( true );
}, [] );
const contextValue = {
subscriptions,
setSubscriptions,
loadSubscriptions,
loadSubscriptions: () => loadSubscriptions(),
isLoading,
setIsLoading,
};

View File

@ -13,7 +13,7 @@ export type MarketplaceContextType = {
export type SubscriptionsContextType = {
subscriptions: Subscription[];
setSubscriptions: ( subscriptions: Subscription[] ) => void;
loadSubscriptions: ( toggleLoading?: boolean ) => void;
loadSubscriptions: ( toggleLoading?: boolean ) => Promise< void >;
isLoading: boolean;
setIsLoading: ( isLoading: boolean ) => void;
};

View File

@ -6,20 +6,20 @@ import apiFetch from '@wordpress/api-fetch';
/**
* Internal dependencies
*/
import { LOCALE } from '../../utils/admin-settings';
import { CategoryAPIItem } from '../components/category-selector/types';
import {
MARKETPLACE_CATEGORY_API_PATH,
MARKETPLACE_HOST,
MARKETPLACE_SEARCH_API_PATH,
} from '../components/constants';
import { Subscription } from '../components/my-subscriptions/types';
import {
Product,
ProductType,
SearchAPIProductType,
SearchAPIJSONType,
SearchAPIProductType,
} from '../components/product-list/types';
import {
MARKETPLACE_HOST,
MARKETPLACE_CATEGORY_API_PATH,
MARKETPLACE_SEARCH_API_PATH,
} from '../components/constants';
import { CategoryAPIItem } from '../components/category-selector/types';
import { LOCALE } from '../../utils/admin-settings';
import { Subscription } from '../components/my-subscriptions/types';
interface ProductGroup {
id: string;
@ -199,6 +199,20 @@ function installProduct( productKey: string ): Promise< void > {
} );
}
function connectProduct( productKey: string ): Promise< void > {
const url = '/wc/v3/marketplace/subscriptions/activate';
const data = new URLSearchParams();
data.append( 'product_key', productKey );
return apiFetch( {
path: url.toString(),
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: data,
} );
}
// Append UTM parameters to a URL, being aware of existing query parameters
const appendURLParams = (
url: string,
@ -219,11 +233,12 @@ const appendURLParams = (
};
export {
fetchSearchResults,
fetchDiscoverPageData,
fetchCategories,
fetchSubscriptions,
installProduct,
ProductGroup,
appendURLParams,
connectProduct,
fetchCategories,
fetchDiscoverPageData,
fetchSearchResults,
fetchSubscriptions,
installProduct,
};

View File

@ -36,6 +36,15 @@ declare global {
'shipping-setting-tour': boolean;
};
wp: {
updates?: {
ajax: ( action, data: {
slug: string;
plugin?: string;
theme?: string;
success?: function;
error?: function;
} ) => JQuery.Promise;
};
autosave?: {
server: {
postChanged: () => boolean;

View File

@ -0,0 +1,4 @@
Significance: patch
Type: add
Add update support to the My subscriptions page.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: add
Comment: Add my subscription action buttons and modals.

View File

@ -1522,6 +1522,9 @@ class WC_Helper {
'installed' => false,
'active' => false,
'version' => null,
'type' => null,
'slug' => null,
'path' => null,
);
$updates = WC_Helper_Updater::get_update_data();
@ -1538,14 +1541,19 @@ class WC_Helper {
$subscription['local']['installed'] = true;
$subscription['local']['version'] = $local['Version'];
$subscription['local']['type'] = $local['_type'];
$subscription['local']['path'] = $local['_filename'];
$subscription['local']['slug'] = null;
if ( 'plugin' === $local['_type'] ) {
$subscription['local']['slug'] = $local['slug'];
if ( is_plugin_active( $local['_filename'] ) ) {
$subscription['local']['active'] = true;
} elseif ( is_multisite() && is_plugin_active_for_network( $local['_filename'] ) ) {
$subscription['local']['active'] = true;
}
} elseif ( 'theme' === $local['_type'] ) {
$subscription['local']['slug'] = $local['_stylesheet'];
if ( in_array( $local['_stylesheet'], array( get_stylesheet(), get_template() ), true ) ) {
$subscription['local']['active'] = true;
}

View File

@ -18,6 +18,7 @@ class Marketplace {
final public function init() {
if ( FeaturesUtil::feature_is_enabled( 'marketplace' ) ) {
add_action( 'admin_menu', array( $this, 'register_pages' ), 70 );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
}
}
@ -53,4 +54,21 @@ class Marketplace {
*/
return apply_filters( 'woocommerce_marketplace_menu_items', $marketplace_pages );
}
/**
* Enqueue update script.
*
* @param string $hook_suffix The current admin page.
*/
public function enqueue_scripts( $hook_suffix ) {
if ( 'woocommerce_page_wc-admin' !== $hook_suffix ) {
return;
};
if ( ! isset( $_GET['path'] ) || '/extensions' !== $_GET['path'] ) {
return;
}
// Enqueue WordPress updates script to enable plugin and theme installs and updates.
wp_enqueue_script( 'updates' );
}
}