Migrate LYS user meta (#50664)
* Migrate lys meta * Update lys meta logic * Update user preferences types and logic * Add changelog * Revert changes * Fix types * Fix logic * Fix lint
This commit is contained in:
parent
403d78ae22
commit
deaca578cc
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: update
|
||||
|
||||
Update user preferences types and logic
|
|
@ -33,13 +33,12 @@ export type UserPreferences = {
|
|||
product_advice_card_dismissed?: {
|
||||
[ key: string ]: 'yes' | 'no';
|
||||
};
|
||||
launch_your_store_tour_hidden?: 'yes' | 'no' | '';
|
||||
coming_soon_banner_dismissed?: 'yes' | 'no' | '';
|
||||
};
|
||||
|
||||
export type WoocommerceMeta = UserPreferences & {
|
||||
task_list_tracked_started_tasks?: string;
|
||||
variable_items_without_price_notice_dismissed?: string;
|
||||
local_attributes_notice_dismissed_ids?: string;
|
||||
product_advice_card_dismissed?: string;
|
||||
export type WoocommerceMeta = {
|
||||
[ key in keyof UserPreferences ]: string;
|
||||
};
|
||||
|
||||
export type WCUser<
|
||||
|
|
|
@ -19,28 +19,15 @@ import { WCUser, UserPreferences } from './types';
|
|||
const getWooCommerceMeta = ( user: WCUser ) => {
|
||||
const wooMeta = user.woocommerce_meta || {};
|
||||
|
||||
const userData = mapValues( wooMeta, ( data, key ) => {
|
||||
const userData = mapValues( wooMeta, ( data ) => {
|
||||
if ( ! data || data.length === 0 ) {
|
||||
return '';
|
||||
}
|
||||
try {
|
||||
return JSON.parse( data );
|
||||
} catch ( e ) {
|
||||
if ( e instanceof Error ) {
|
||||
/* eslint-disable no-console */
|
||||
console.error(
|
||||
`Error parsing value '${ data }' for ${ key }`,
|
||||
e.message
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
} else {
|
||||
/* eslint-disable no-console */
|
||||
console.error(
|
||||
`Unexpected Error parsing value '${ data }' for ${ key } ${ e }`
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
return '';
|
||||
// If we can't parse the value, return the raw data. The meta value could be a string like 'yes' or 'no'.
|
||||
return data;
|
||||
}
|
||||
} );
|
||||
|
||||
|
@ -53,7 +40,7 @@ async function updateUserPrefs(
|
|||
user: WCUser,
|
||||
saveUser: ( userToSave: {
|
||||
id: number;
|
||||
woocommerce_meta: { [ key: string ]: boolean };
|
||||
woocommerce_meta: WCUser[ 'woocommerce_meta' ];
|
||||
} ) => WCUser,
|
||||
getLastEntitySaveError: (
|
||||
kind: string,
|
||||
|
@ -64,7 +51,14 @@ async function updateUserPrefs(
|
|||
) {
|
||||
// @todo Handle unresolved getCurrentUser() here.
|
||||
// Prep fields for update.
|
||||
const metaData = mapValues( userPrefs, JSON.stringify );
|
||||
const metaData = mapValues( userPrefs, ( value ) => {
|
||||
if ( typeof value === 'string' ) {
|
||||
// If the value is a string, we don't need to serialize it.
|
||||
return value;
|
||||
}
|
||||
|
||||
return JSON.stringify( value );
|
||||
} );
|
||||
|
||||
if ( Object.keys( metaData ).length === 0 ) {
|
||||
return {
|
||||
|
@ -81,7 +75,6 @@ async function updateUserPrefs(
|
|||
...metaData,
|
||||
},
|
||||
} );
|
||||
|
||||
// Use saveUser() to update WooCommerce meta values.
|
||||
const updatedUser = await saveUser( {
|
||||
id: user.id,
|
||||
|
|
|
@ -1,50 +1,57 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||
import { useSelect, dispatch } from '@wordpress/data';
|
||||
import { OPTIONS_STORE_NAME, useUserPreferences } from '@woocommerce/data';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useState } from 'react';
|
||||
|
||||
const LYS_TOUR_HIDDEN = 'woocommerce_launch_your_store_tour_hidden';
|
||||
|
||||
export const useSiteVisibilityTour = () => {
|
||||
const [ showTour, setShowTour ] = useState( true );
|
||||
const { shouldTourBeShown } = useSelect( ( select ) => {
|
||||
// Tour should only be shown if the user has not seen it before and the `woocommerce_show_lys_tour` option is "yes" (for sites upgrading from a previous WooCommerce version)
|
||||
|
||||
const { getCurrentUser } = select( 'core' );
|
||||
const wasTourShown =
|
||||
// Tour should only be shown if the user has not seen it before and the `woocommerce_show_lys_tour` option is "yes" (for sites upgrading from a previous WooCommerce version)
|
||||
const shouldStoreShowLYSTour = useSelect(
|
||||
( select ) =>
|
||||
select( OPTIONS_STORE_NAME ).getOption(
|
||||
'woocommerce_show_lys_tour'
|
||||
) === 'yes'
|
||||
);
|
||||
|
||||
/**
|
||||
* This is temporary to support sites upgrading from a previous version of WooCommerce.
|
||||
* We used user meta to store the tour dismissal state but now we use WooCommerce meta instead.
|
||||
* It will be removed in WC 9.4.
|
||||
*/
|
||||
const hasUserDismissedTourMeta = useSelect( ( select ) => {
|
||||
const currentUser = select( 'core' ).getCurrentUser();
|
||||
if ( ! currentUser ) {
|
||||
// If the user is not logged in, we don't want to show the tour.
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
( getCurrentUser() as { meta?: { [ key: string ]: string } } )
|
||||
?.meta?.[ LYS_TOUR_HIDDEN ] === 'yes';
|
||||
|
||||
const { getOption } = select( OPTIONS_STORE_NAME );
|
||||
|
||||
const showLYSTourOption = getOption( 'woocommerce_show_lys_tour' );
|
||||
|
||||
const _shouldTourBeShown =
|
||||
showLYSTourOption === 'yes' && ! wasTourShown;
|
||||
|
||||
return {
|
||||
shouldTourBeShown: _shouldTourBeShown,
|
||||
};
|
||||
( currentUser as { meta: { [ key: string ]: string } } ).meta
|
||||
.woocommerce_launch_your_store_tour_hidden === 'yes'
|
||||
);
|
||||
} );
|
||||
|
||||
const {
|
||||
launch_your_store_tour_hidden: lysTourHidden,
|
||||
updateUserPreferences,
|
||||
} = useUserPreferences();
|
||||
|
||||
const onClose = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
dispatch( 'core' ).saveUser( {
|
||||
id: window?.wcSettings?.currentUserId,
|
||||
meta: {
|
||||
woocommerce_launch_your_store_tour_hidden: 'yes',
|
||||
},
|
||||
updateUserPreferences( {
|
||||
launch_your_store_tour_hidden: 'yes',
|
||||
} );
|
||||
};
|
||||
|
||||
return {
|
||||
onClose,
|
||||
shouldTourBeShown,
|
||||
shouldTourBeShown:
|
||||
shouldStoreShowLYSTour &&
|
||||
! ( hasUserDismissedTourMeta || lysTourHidden ),
|
||||
showTour,
|
||||
setShowTour,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: update
|
||||
|
||||
Migrate LYS user meta
|
|
@ -1,20 +1,20 @@
|
|||
/* global Cookies */
|
||||
jQuery( function( $ ) {
|
||||
jQuery( function ( $ ) {
|
||||
// Orderby
|
||||
$( '.woocommerce-ordering' ).on( 'change', 'select.orderby', function() {
|
||||
$( '.woocommerce-ordering' ).on( 'change', 'select.orderby', function () {
|
||||
$( this ).closest( 'form' ).trigger( 'submit' );
|
||||
});
|
||||
} );
|
||||
|
||||
// Target quantity inputs on product pages
|
||||
$( 'input.qty:not(.product-quantity input.qty)' ).each( function() {
|
||||
$( 'input.qty:not(.product-quantity input.qty)' ).each( function () {
|
||||
var min = parseFloat( $( this ).attr( 'min' ) );
|
||||
|
||||
if ( min >= 0 && parseFloat( $( this ).val() ) < min ) {
|
||||
$( this ).val( min );
|
||||
}
|
||||
});
|
||||
} );
|
||||
|
||||
var noticeID = $( '.woocommerce-store-notice' ).data( 'noticeId' ) || '',
|
||||
var noticeID = $( '.woocommerce-store-notice' ).data( 'noticeId' ) || '',
|
||||
cookieName = 'store_notice' + noticeID;
|
||||
|
||||
// Check the value of that cookie and show/hide the notice accordingly
|
||||
|
@ -25,43 +25,56 @@ jQuery( function( $ ) {
|
|||
}
|
||||
|
||||
// Set a cookie and hide the store notice when the dismiss button is clicked
|
||||
$( '.woocommerce-store-notice__dismiss-link' ).on( 'click', function( event ) {
|
||||
Cookies.set( cookieName, 'hidden', { path: '/' } );
|
||||
$( '.woocommerce-store-notice' ).hide();
|
||||
event.preventDefault();
|
||||
});
|
||||
$( '.woocommerce-store-notice__dismiss-link' ).on(
|
||||
'click',
|
||||
function ( event ) {
|
||||
Cookies.set( cookieName, 'hidden', { path: '/' } );
|
||||
$( '.woocommerce-store-notice' ).hide();
|
||||
event.preventDefault();
|
||||
}
|
||||
);
|
||||
|
||||
// Make form field descriptions toggle on focus.
|
||||
if ( $( '.woocommerce-input-wrapper span.description' ).length ) {
|
||||
$( document.body ).on( 'click', function() {
|
||||
$( '.woocommerce-input-wrapper span.description:visible' ).prop( 'aria-hidden', true ).slideUp( 250 );
|
||||
$( document.body ).on( 'click', function () {
|
||||
$( '.woocommerce-input-wrapper span.description:visible' )
|
||||
.prop( 'aria-hidden', true )
|
||||
.slideUp( 250 );
|
||||
} );
|
||||
}
|
||||
|
||||
$( '.woocommerce-input-wrapper' ).on( 'click', function( event ) {
|
||||
$( '.woocommerce-input-wrapper' ).on( 'click', function ( event ) {
|
||||
event.stopPropagation();
|
||||
} );
|
||||
|
||||
$( '.woocommerce-input-wrapper :input' )
|
||||
.on( 'keydown', function( event ) {
|
||||
var input = $( this ),
|
||||
parent = input.parent(),
|
||||
.on( 'keydown', function ( event ) {
|
||||
var input = $( this ),
|
||||
parent = input.parent(),
|
||||
description = parent.find( 'span.description' );
|
||||
|
||||
if ( 27 === event.which && description.length && description.is( ':visible' ) ) {
|
||||
if (
|
||||
27 === event.which &&
|
||||
description.length &&
|
||||
description.is( ':visible' )
|
||||
) {
|
||||
description.prop( 'aria-hidden', true ).slideUp( 250 );
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
} )
|
||||
.on( 'click focus', function() {
|
||||
var input = $( this ),
|
||||
parent = input.parent(),
|
||||
.on( 'click focus', function () {
|
||||
var input = $( this ),
|
||||
parent = input.parent(),
|
||||
description = parent.find( 'span.description' );
|
||||
|
||||
parent.addClass( 'currentTarget' );
|
||||
|
||||
$( '.woocommerce-input-wrapper:not(.currentTarget) span.description:visible' ).prop( 'aria-hidden', true ).slideUp( 250 );
|
||||
$(
|
||||
'.woocommerce-input-wrapper:not(.currentTarget) span.description:visible'
|
||||
)
|
||||
.prop( 'aria-hidden', true )
|
||||
.slideUp( 250 );
|
||||
|
||||
if ( description.length && description.is( ':hidden' ) ) {
|
||||
description.prop( 'aria-hidden', false ).slideDown( 250 );
|
||||
|
@ -71,52 +84,66 @@ jQuery( function( $ ) {
|
|||
} );
|
||||
|
||||
// Common scroll to element code.
|
||||
$.scroll_to_notices = function( scrollElement ) {
|
||||
$.scroll_to_notices = function ( scrollElement ) {
|
||||
if ( scrollElement.length ) {
|
||||
$( 'html, body' ).animate( {
|
||||
scrollTop: ( scrollElement.offset().top - 100 )
|
||||
}, 1000 );
|
||||
$( 'html, body' ).animate(
|
||||
{
|
||||
scrollTop: scrollElement.offset().top - 100,
|
||||
},
|
||||
1000
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Show password visibility hover icon on woocommerce forms
|
||||
$( '.woocommerce form .woocommerce-Input[type="password"]' ).wrap( '<span class="password-input"></span>' );
|
||||
$( '.woocommerce form .woocommerce-Input[type="password"]' ).wrap(
|
||||
'<span class="password-input"></span>'
|
||||
);
|
||||
// Add 'password-input' class to the password wrapper in checkout page.
|
||||
$( '.woocommerce form input' ).filter(':password').parent('span').addClass('password-input');
|
||||
$( '.password-input' ).append( '<span class="show-password-input"></span>' );
|
||||
|
||||
$( '.show-password-input' ).on( 'click',
|
||||
function() {
|
||||
if ( $( this ).hasClass( 'display-password' ) ) {
|
||||
$( this ).removeClass( 'display-password' );
|
||||
} else {
|
||||
$( this ).addClass( 'display-password' );
|
||||
}
|
||||
if ( $( this ).hasClass( 'display-password' ) ) {
|
||||
$( this ).siblings( ['input[type="password"]'] ).prop( 'type', 'text' );
|
||||
} else {
|
||||
$( this ).siblings( 'input[type="text"]' ).prop( 'type', 'password' );
|
||||
}
|
||||
}
|
||||
$( '.woocommerce form input' )
|
||||
.filter( ':password' )
|
||||
.parent( 'span' )
|
||||
.addClass( 'password-input' );
|
||||
$( '.password-input' ).append(
|
||||
'<span class="show-password-input"></span>'
|
||||
);
|
||||
|
||||
$( '.show-password-input' ).on( 'click', function () {
|
||||
if ( $( this ).hasClass( 'display-password' ) ) {
|
||||
$( this ).removeClass( 'display-password' );
|
||||
} else {
|
||||
$( this ).addClass( 'display-password' );
|
||||
}
|
||||
if ( $( this ).hasClass( 'display-password' ) ) {
|
||||
$( this )
|
||||
.siblings( [ 'input[type="password"]' ] )
|
||||
.prop( 'type', 'text' );
|
||||
} else {
|
||||
$( this )
|
||||
.siblings( 'input[type="text"]' )
|
||||
.prop( 'type', 'password' );
|
||||
}
|
||||
} );
|
||||
|
||||
$( 'a.coming-soon-footer-banner-dismiss' ).on( 'click', function( e ) {
|
||||
$( 'a.coming-soon-footer-banner-dismiss' ).on( 'click', function ( e ) {
|
||||
var target = $( e.target );
|
||||
$.ajax( {
|
||||
type: 'post',
|
||||
url: target.data( 'rest-url' ),
|
||||
data: {
|
||||
meta: {
|
||||
'woocommerce_coming_soon_banner_dismissed': 'yes'
|
||||
}
|
||||
woocommerce_meta: {
|
||||
coming_soon_banner_dismissed: 'yes',
|
||||
},
|
||||
},
|
||||
beforeSend: function ( xhr ) {
|
||||
xhr.setRequestHeader( 'X-WP-Nonce', target.data( 'rest-nonce' ) );
|
||||
xhr.setRequestHeader(
|
||||
'X-WP-Nonce',
|
||||
target.data( 'rest-nonce' )
|
||||
);
|
||||
},
|
||||
complete: function () {
|
||||
$('#coming-soon-footer-banner').hide();
|
||||
}
|
||||
$( '#coming-soon-footer-banner' ).hide();
|
||||
},
|
||||
} );
|
||||
} );
|
||||
});
|
||||
} );
|
||||
|
|
|
@ -264,6 +264,7 @@ class WC_Install {
|
|||
),
|
||||
'9.3.0' => array(
|
||||
'wc_update_930_add_woocommerce_coming_soon_option',
|
||||
'wc_update_930_migrate_user_meta_for_launch_your_store_tour',
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -2824,3 +2824,31 @@ function wc_update_910_remove_obsolete_user_meta() {
|
|||
function wc_update_930_add_woocommerce_coming_soon_option() {
|
||||
add_option( 'woocommerce_coming_soon', 'no' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate Launch Your Store tour meta keys to the woocommerce_meta user data fields.
|
||||
*/
|
||||
function wc_update_930_migrate_user_meta_for_launch_your_store_tour() {
|
||||
// Rename `woocommerce_launch_your_store_tour_hidden` meta key to `woocommerce_admin_launch_your_store_tour_hidden`.
|
||||
global $wpdb;
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"UPDATE {$wpdb->usermeta}
|
||||
SET meta_key = %s
|
||||
WHERE meta_key = %s",
|
||||
'woocommerce_admin_launch_your_store_tour_hidden',
|
||||
'woocommerce_launch_your_store_tour_hidden'
|
||||
)
|
||||
);
|
||||
|
||||
// Rename `woocommerce_coming_soon_banner_dismissed` meta key to `woocommerce_admin_coming_soon_banner_dismissed`.
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"UPDATE {$wpdb->usermeta}
|
||||
SET meta_key = %s
|
||||
WHERE meta_key = %s",
|
||||
'woocommerce_admin_coming_soon_banner_dismissed',
|
||||
'woocommerce_coming_soon_banner_dismissed'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ namespace Automattic\WooCommerce\Admin\Features;
|
|||
use Automattic\WooCommerce\Admin\PageController;
|
||||
use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
|
||||
use Automattic\WooCommerce\Admin\WCAdminHelper;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminUser;
|
||||
|
||||
/**
|
||||
* Takes care of Launch Your Store related actions.
|
||||
*/
|
||||
class LaunchYourStore {
|
||||
const BANNER_DISMISS_USER_META_KEY = 'woocommerce_coming_soon_banner_dismissed';
|
||||
const BANNER_DISMISS_USER_META_KEY = 'coming_soon_banner_dismissed';
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
|
@ -21,6 +22,7 @@ class LaunchYourStore {
|
|||
add_action( 'init', array( $this, 'register_launch_your_store_user_meta_fields' ) );
|
||||
add_filter( 'woocommerce_tracks_event_properties', array( $this, 'append_coming_soon_global_tracks' ), 10, 2 );
|
||||
add_action( 'wp_login', array( $this, 'reset_woocommerce_coming_soon_banner_dismissed' ), 10, 2 );
|
||||
add_filter( 'woocommerce_admin_get_user_data_fields', array( $this, 'add_user_data_fields' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,7 +162,10 @@ class LaunchYourStore {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ( get_user_meta( $current_user_id, self::BANNER_DISMISS_USER_META_KEY, true ) === 'yes' ) {
|
||||
$has_dismissed_banner = WCAdminUser::get_user_data_field( $current_user_id, self::BANNER_DISMISS_USER_META_KEY )
|
||||
// Remove this check in WC 9.4.
|
||||
|| get_user_meta( $current_user_id, 'woocommerce_' . self::BANNER_DISMISS_USER_META_KEY, true ) === 'yes';
|
||||
if ( $has_dismissed_banner ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -198,6 +203,8 @@ class LaunchYourStore {
|
|||
|
||||
/**
|
||||
* Register user meta fields for Launch Your Store.
|
||||
*
|
||||
* This should be removed in WC 9.4.
|
||||
*/
|
||||
public function register_launch_your_store_user_meta_fields() {
|
||||
if ( ! $this->is_manager_or_admin() ) {
|
||||
|
@ -217,7 +224,7 @@ class LaunchYourStore {
|
|||
|
||||
register_meta(
|
||||
'user',
|
||||
self::BANNER_DISMISS_USER_META_KEY,
|
||||
'woocommerce_coming_soon_banner_dismissed',
|
||||
array(
|
||||
'type' => 'string',
|
||||
'description' => 'Indicate whether the user has dismissed the coming soon notice or not.',
|
||||
|
@ -227,6 +234,22 @@ class LaunchYourStore {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register user meta fields for Launch Your Store.
|
||||
*
|
||||
* @param array $user_data_fields user data fields.
|
||||
* @return array
|
||||
*/
|
||||
public function add_user_data_fields( $user_data_fields ) {
|
||||
return array_merge(
|
||||
$user_data_fields,
|
||||
array(
|
||||
'launch_your_store_tour_hidden',
|
||||
self::BANNER_DISMISS_USER_META_KEY,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset 'woocommerce_coming_soon_banner_dismissed' user meta to 'no'.
|
||||
*
|
||||
|
@ -236,9 +259,9 @@ class LaunchYourStore {
|
|||
* @param object $user user object.
|
||||
*/
|
||||
public function reset_woocommerce_coming_soon_banner_dismissed( $user_login, $user ) {
|
||||
$existing_meta = get_user_meta( $user->ID, self::BANNER_DISMISS_USER_META_KEY, true );
|
||||
$existing_meta = WCAdminUser::get_user_data_field( $user->ID, self::BANNER_DISMISS_USER_META_KEY );
|
||||
if ( 'yes' === $existing_meta ) {
|
||||
update_user_meta( $user->ID, self::BANNER_DISMISS_USER_META_KEY, 'no' );
|
||||
WCAdminUser::update_user_data_field( $user->ID, self::BANNER_DISMISS_USER_META_KEY, 'no' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue