mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Settings UI: Add Todos saving and dismissing
This commit is contained in:
parent
0daf56b2af
commit
f6717e2e66
16 changed files with 433 additions and 89 deletions
|
@ -1,5 +1,7 @@
|
|||
import { selectTab, TAB_IDS } from '../../../utils/tabSelector';
|
||||
import { useState } from '@wordpress/element';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { STORE_NAME as TODOS_STORE_NAME } from '../../../data/todos';
|
||||
|
||||
const TodoSettingsBlock = ( {
|
||||
todosData,
|
||||
|
@ -9,6 +11,21 @@ const TodoSettingsBlock = ( {
|
|||
onDismissTodo,
|
||||
} ) => {
|
||||
const [ dismissingIds, setDismissingIds ] = useState( new Set() );
|
||||
const { completedTodos, dismissedTodos } = useSelect(
|
||||
( select ) => ( {
|
||||
completedTodos:
|
||||
select( TODOS_STORE_NAME ).getCompletedTodos() || [],
|
||||
dismissedTodos:
|
||||
select( TODOS_STORE_NAME ).getDismissedTodos() || [],
|
||||
} ),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect( () => {
|
||||
if ( dismissedTodos.length === 0 ) {
|
||||
setDismissingIds( new Set() );
|
||||
}
|
||||
}, [ dismissedTodos ] );
|
||||
|
||||
if ( todosData.length === 0 ) {
|
||||
return null;
|
||||
|
@ -24,17 +41,22 @@ const TodoSettingsBlock = ( {
|
|||
}, 300 );
|
||||
};
|
||||
|
||||
// Filter out dismissed todos for display
|
||||
const visibleTodos = todosData.filter(
|
||||
( todo ) => ! dismissedTodos.includes( todo.id )
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ `ppcp-r-settings-block__todo ppcp-r-todo-items ${ className }` }
|
||||
>
|
||||
{ todosData.map( ( todo ) => (
|
||||
{ visibleTodos.map( ( todo ) => (
|
||||
<TodoItem
|
||||
key={ todo.id }
|
||||
id={ todo.id }
|
||||
title={ todo.title }
|
||||
description={ todo.description }
|
||||
isCompleted={ todo.isCompleted }
|
||||
isCompleted={ completedTodos.includes( todo.id ) }
|
||||
isDismissing={ dismissingIds.has( todo.id ) }
|
||||
onDismiss={ ( e ) => handleDismiss( todo.id, e ) }
|
||||
onClick={ async () => {
|
||||
|
|
|
@ -14,7 +14,9 @@ import SettingsCard from '../../../ReusableComponents/SettingsCard';
|
|||
import { TITLE_BADGE_POSITIVE } from '../../../ReusableComponents/TitleBadge';
|
||||
import { useTodos } from '../../../../data/todos/hooks';
|
||||
import { useMerchantInfo } from '../../../../data/common/hooks';
|
||||
import { STORE_NAME } from '../../../../data/common';
|
||||
import { STORE_NAME as COMMON_STORE_NAME } from '../../../../data/common';
|
||||
import { STORE_NAME as TODOS_STORE_NAME } from '../../../../data/todos';
|
||||
|
||||
import { getFeatures } from '../Components/Overview/features-config';
|
||||
|
||||
import {
|
||||
|
@ -35,13 +37,34 @@ const TabOverview = () => {
|
|||
export default TabOverview;
|
||||
|
||||
const OverviewTodos = () => {
|
||||
const [ isResetting, setIsResetting ] = useState( false );
|
||||
const { todos, isReady: areTodosReady, dismissTodo } = useTodos();
|
||||
const { setActiveModal } = useDispatch( STORE_NAME );
|
||||
const { setActiveHighlight } = useDispatch( STORE_NAME );
|
||||
const { setActiveModal, setActiveHighlight } =
|
||||
useDispatch( COMMON_STORE_NAME );
|
||||
const { resetDismissedTodos, setDismissedTodos } =
|
||||
useDispatch( TODOS_STORE_NAME );
|
||||
const { createSuccessNotice } = useDispatch( noticesStore );
|
||||
|
||||
// Don't render todos section until data is ready
|
||||
const showTodos = areTodosReady && todos.length > 0;
|
||||
|
||||
const resetHandler = async () => {
|
||||
setIsResetting( true );
|
||||
try {
|
||||
await setDismissedTodos( [] );
|
||||
await resetDismissedTodos();
|
||||
|
||||
createSuccessNotice(
|
||||
__(
|
||||
'Dismissed items restored successfully.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
{ icon: NOTIFICATION_SUCCESS }
|
||||
);
|
||||
} finally {
|
||||
setIsResetting( false );
|
||||
}
|
||||
};
|
||||
|
||||
if ( ! showTodos ) {
|
||||
return null;
|
||||
}
|
||||
|
@ -50,10 +73,29 @@ const OverviewTodos = () => {
|
|||
<SettingsCard
|
||||
className="ppcp-r-tab-overview-todo"
|
||||
title={ __( 'Things to do next', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Complete these tasks to keep your store updated with the latest products and services.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={
|
||||
<>
|
||||
<p>
|
||||
{ __(
|
||||
'Complete these tasks to keep your store updated with the latest products and services.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</p>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={ resetHandler }
|
||||
disabled={ isResetting }
|
||||
>
|
||||
<Icon icon={ reusableBlock } size={ 18 } />
|
||||
{ isResetting
|
||||
? __( 'Restoring…', 'woocommerce-paypal-payments' )
|
||||
: __(
|
||||
'Restore dismissed Things To Do',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<TodoSettingsBlock
|
||||
todosData={ todos }
|
||||
|
@ -69,7 +111,7 @@ const OverviewFeatures = () => {
|
|||
const [ isRefreshing, setIsRefreshing ] = useState( false );
|
||||
const { merchant, features: merchantFeatures } = useMerchantInfo();
|
||||
const { refreshFeatureStatuses, setActiveModal } =
|
||||
useDispatch( STORE_NAME );
|
||||
useDispatch( COMMON_STORE_NAME );
|
||||
const { createSuccessNotice, createErrorNotice } =
|
||||
useDispatch( noticesStore );
|
||||
|
||||
|
@ -79,7 +121,7 @@ const OverviewFeatures = () => {
|
|||
[ setActiveModal ]
|
||||
);
|
||||
|
||||
// Map merchant features status to our config
|
||||
// Map merchant features status to the config
|
||||
const features = useMemo( () => {
|
||||
return featuresData.map( ( feature ) => {
|
||||
const merchantFeature = merchantFeatures?.[ feature.id ];
|
||||
|
|
|
@ -23,14 +23,19 @@ export const initTodoSync = () => {
|
|||
|
||||
try {
|
||||
const paymentState = select( 'wc/paypal/payment' ).persistentData();
|
||||
const todosState = select( 'wc/paypal/todos' ).getTodos();
|
||||
const completedTodos =
|
||||
select( 'wc/paypal/todos' ).getCompletedTodos();
|
||||
|
||||
// Skip if states haven't been initialized yet
|
||||
if ( ! paymentState || ! todosState || ! previousPaymentState ) {
|
||||
if ( ! paymentState || ! previousPaymentState ) {
|
||||
previousPaymentState = paymentState;
|
||||
isProcessing = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Track which todos should be marked as completed
|
||||
let newCompletedTodos = [ ...( completedTodos || [] ) ];
|
||||
|
||||
Object.entries( TODO_TRIGGERS ).forEach(
|
||||
( [ paymentMethod, todoId ] ) => {
|
||||
const wasEnabled =
|
||||
|
@ -38,26 +43,34 @@ export const initTodoSync = () => {
|
|||
const isEnabled = paymentState[ paymentMethod ]?.enabled;
|
||||
|
||||
if ( wasEnabled !== isEnabled ) {
|
||||
const todoToUpdate = todosState.find(
|
||||
( todo ) => todo.id === todoId
|
||||
);
|
||||
|
||||
if ( todoToUpdate ) {
|
||||
const updatedTodos = todosState.map( ( todo ) =>
|
||||
todo.id === todoId
|
||||
? { ...todo, isCompleted: isEnabled }
|
||||
: todo
|
||||
);
|
||||
|
||||
dispatch( 'wc/paypal/todos' ).setTodos(
|
||||
updatedTodos
|
||||
if ( isEnabled ) {
|
||||
// Add to completed todos if not already there
|
||||
if ( ! newCompletedTodos.includes( todoId ) ) {
|
||||
newCompletedTodos.push( todoId );
|
||||
}
|
||||
} else {
|
||||
// Remove from completed todos
|
||||
newCompletedTodos = newCompletedTodos.filter(
|
||||
( id ) => id !== todoId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
previousPaymentState = paymentState;
|
||||
// Only dispatch if there are changes
|
||||
if (
|
||||
newCompletedTodos.length !== completedTodos.length ||
|
||||
newCompletedTodos.some(
|
||||
( id ) => ! completedTodos.includes( id )
|
||||
)
|
||||
) {
|
||||
dispatch( 'wc/paypal/todos' ).setCompletedTodos(
|
||||
newCompletedTodos
|
||||
);
|
||||
}
|
||||
|
||||
previousPaymentState = { ...paymentState };
|
||||
} catch ( error ) {
|
||||
console.error( 'Error in todo sync:', error );
|
||||
} finally {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
export default {
|
||||
// Transient data
|
||||
SET_TRANSIENT: 'TODOS:SET_TRANSIENT',
|
||||
SET_COMPLETED_TODOS: 'TODOS:SET_COMPLETED_TODOS',
|
||||
|
||||
// Persistent data
|
||||
SET_TODOS: 'TODOS:SET_TODOS',
|
||||
|
@ -14,4 +15,6 @@ export default {
|
|||
|
||||
// Controls
|
||||
DO_FETCH_TODOS: 'TODOS:DO_FETCH_TODOS',
|
||||
DO_PERSIST_DATA: 'TODOS:DO_PERSIST_DATA',
|
||||
DO_RESET_DISMISSED_TODOS: 'TODOS:DO_RESET_DISMISSED_TODOS',
|
||||
};
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
* @file
|
||||
*/
|
||||
|
||||
import { select } from '@wordpress/data';
|
||||
import ACTION_TYPES from './action-types';
|
||||
import { STORE_NAME } from './constants';
|
||||
|
||||
export const setIsReady = ( isReady ) => ( {
|
||||
type: ACTION_TYPES.SET_TRANSIENT,
|
||||
|
@ -27,3 +29,24 @@ export const setDismissedTodos = ( dismissedTodos ) => ( {
|
|||
export const fetchTodos = function* () {
|
||||
yield { type: ACTION_TYPES.DO_FETCH_TODOS };
|
||||
};
|
||||
|
||||
export const persist = function* () {
|
||||
const data = yield select( STORE_NAME ).persistentData();
|
||||
yield { type: ACTION_TYPES.DO_PERSIST_DATA, data };
|
||||
};
|
||||
|
||||
export const resetDismissedTodos = function* () {
|
||||
const result = yield { type: ACTION_TYPES.DO_RESET_DISMISSED_TODOS };
|
||||
|
||||
if ( result && result.success ) {
|
||||
// After successful reset, fetch fresh todos
|
||||
yield fetchTodos();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const setCompletedTodos = ( completedTodos ) => ( {
|
||||
type: ACTION_TYPES.SET_COMPLETED_TODOS,
|
||||
payload: completedTodos,
|
||||
} );
|
||||
|
|
|
@ -6,3 +6,6 @@
|
|||
|
||||
export const STORE_NAME = 'wc/paypal/todos';
|
||||
export const REST_PATH = '/wc/v3/wc_paypal/todos';
|
||||
export const REST_PERSIST_PATH = '/wc/v3/wc_paypal/todos';
|
||||
export const REST_RESET_DISMISSED_TODOS_PATH =
|
||||
'/wc/v3/wc_paypal/reset-dismissed-todos';
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
*/
|
||||
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
import { REST_PATH } from './constants';
|
||||
import {
|
||||
REST_PATH,
|
||||
REST_PERSIST_PATH,
|
||||
REST_RESET_DISMISSED_TODOS_PATH,
|
||||
} from './constants';
|
||||
import ACTION_TYPES from './action-types';
|
||||
|
||||
export const controls = {
|
||||
|
@ -19,4 +23,25 @@ export const controls = {
|
|||
} );
|
||||
return response?.data || [];
|
||||
},
|
||||
async [ ACTION_TYPES.DO_PERSIST_DATA ]( { data } ) {
|
||||
return await apiFetch( {
|
||||
path: REST_PERSIST_PATH,
|
||||
method: 'POST',
|
||||
data,
|
||||
} );
|
||||
},
|
||||
async [ ACTION_TYPES.DO_RESET_DISMISSED_TODOS ]() {
|
||||
try {
|
||||
return await apiFetch( {
|
||||
path: REST_RESET_DISMISSED_TODOS_PATH,
|
||||
method: 'POST',
|
||||
} );
|
||||
} catch ( e ) {
|
||||
return {
|
||||
success: false,
|
||||
error: e,
|
||||
message: e.message,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -9,45 +9,87 @@
|
|||
|
||||
import { useSelect, useDispatch } from '@wordpress/data';
|
||||
import { STORE_NAME } from './constants';
|
||||
import { createHooksForStore } from '../utils';
|
||||
|
||||
const useTransient = ( key ) =>
|
||||
useSelect(
|
||||
( select ) => select( STORE_NAME ).transientData()?.[ key ],
|
||||
[ key ]
|
||||
);
|
||||
const ensureArray = ( value ) => {
|
||||
if ( ! value ) {
|
||||
return [];
|
||||
}
|
||||
return Array.isArray( value ) ? value : Object.values( value );
|
||||
};
|
||||
|
||||
export const useTodos = () => {
|
||||
const todos = useSelect(
|
||||
( select ) => select( STORE_NAME ).getTodos(),
|
||||
[]
|
||||
);
|
||||
// Convert to array if we get an object
|
||||
const dismissedTodos = useSelect( ( select ) => {
|
||||
const dismissed = select( STORE_NAME ).getDismissedTodos() || [];
|
||||
return Array.isArray( dismissed )
|
||||
? dismissed
|
||||
: Object.values( dismissed );
|
||||
const useHooks = () => {
|
||||
const { useTransient } = createHooksForStore( STORE_NAME );
|
||||
const { fetchTodos, setDismissedTodos, setCompletedTodos, persist } =
|
||||
useDispatch( STORE_NAME );
|
||||
|
||||
// Read-only flags and derived state.
|
||||
const [ isReady ] = useTransient( 'isReady' );
|
||||
|
||||
// Get todos data from store
|
||||
const { todos, dismissedTodos, completedTodos } = useSelect( ( select ) => {
|
||||
const store = select( STORE_NAME );
|
||||
return {
|
||||
todos: ensureArray( store.getTodos() ),
|
||||
dismissedTodos: ensureArray( store.getDismissedTodos() ),
|
||||
completedTodos: ensureArray( store.getCompletedTodos() ),
|
||||
};
|
||||
}, [] );
|
||||
const isReady = useTransient( 'isReady' );
|
||||
|
||||
const { fetchTodos, setDismissedTodos } = useDispatch( STORE_NAME );
|
||||
const dismissedSet = new Set( dismissedTodos );
|
||||
|
||||
const dismissTodo = ( todoId ) => {
|
||||
const currentDismissed = dismissedTodos || [];
|
||||
if ( ! currentDismissed.includes( todoId ) ) {
|
||||
const newDismissedTodos = [ ...currentDismissed, todoId ];
|
||||
setDismissedTodos( newDismissedTodos );
|
||||
const dismissTodo = async ( todoId ) => {
|
||||
if ( ! dismissedSet.has( todoId ) ) {
|
||||
const newDismissedTodos = [ ...dismissedTodos, todoId ];
|
||||
await setDismissedTodos( newDismissedTodos );
|
||||
}
|
||||
};
|
||||
|
||||
const setTodoCompleted = async ( todoId, isCompleted ) => {
|
||||
let newCompletedTodos;
|
||||
if ( isCompleted ) {
|
||||
newCompletedTodos = [ ...completedTodos, todoId ];
|
||||
} else {
|
||||
newCompletedTodos = completedTodos.filter(
|
||||
( id ) => id !== todoId
|
||||
);
|
||||
}
|
||||
await setCompletedTodos( newCompletedTodos );
|
||||
};
|
||||
|
||||
const filteredTodos = todos.filter(
|
||||
( todo ) => ! dismissedTodos.includes( todo.id )
|
||||
( todo ) => ! dismissedSet.has( todo.id )
|
||||
);
|
||||
|
||||
return {
|
||||
todos: filteredTodos,
|
||||
persist,
|
||||
isReady,
|
||||
todos: filteredTodos,
|
||||
dismissedTodos,
|
||||
completedTodos,
|
||||
fetchTodos,
|
||||
dismissTodo,
|
||||
setTodoCompleted,
|
||||
};
|
||||
};
|
||||
|
||||
export const useStore = () => {
|
||||
const { persist, isReady } = useHooks();
|
||||
return { persist, isReady };
|
||||
};
|
||||
|
||||
export const useTodos = () => {
|
||||
const { todos, fetchTodos, dismissTodo, setTodoCompleted, isReady } =
|
||||
useHooks();
|
||||
return { todos, fetchTodos, dismissTodo, setTodoCompleted, isReady };
|
||||
};
|
||||
|
||||
export const useDismissedTodos = () => {
|
||||
const { dismissedTodos } = useHooks();
|
||||
return { dismissedTodos };
|
||||
};
|
||||
|
||||
export const useCompletedTodos = () => {
|
||||
const { completedTodos } = useHooks();
|
||||
return { completedTodos };
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ import ACTION_TYPES from './action-types';
|
|||
*/
|
||||
const defaultTransient = Object.freeze( {
|
||||
isReady: false,
|
||||
completedTodos: [],
|
||||
} );
|
||||
|
||||
/**
|
||||
|
@ -74,6 +75,28 @@ const reducer = createReducer( defaultTransient, defaultPersistent, {
|
|||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates completed todos list while preserving existing entries
|
||||
*
|
||||
* @param {Object} state Current state
|
||||
* @param {Array} payload Array of todo IDs to mark as completed
|
||||
* @return {Object} Updated state
|
||||
*/
|
||||
[ ACTION_TYPES.SET_COMPLETED_TODOS ]: ( state, payload ) => {
|
||||
return changeTransient( state, {
|
||||
completedTodos: Array.isArray( payload ) ? payload : [],
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets dismissed todos list to an empty array
|
||||
* @param {Object} state Current state
|
||||
* @return {Object} Updated state
|
||||
*/
|
||||
[ ACTION_TYPES.DO_RESET_DISMISSED_TODOS ]: ( state ) => {
|
||||
return changePersistent( state, { dismissedTodos: [] } );
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets state to defaults while maintaining initialization status
|
||||
*
|
||||
|
|
|
@ -18,12 +18,11 @@ export const resolvers = {
|
|||
try {
|
||||
const response = yield apiFetch( { path: REST_PATH } );
|
||||
|
||||
// Make sure we're accessing the correct part of the response
|
||||
const todos = response?.data || [];
|
||||
const { todos = [], dismissedTodos = [] } = response?.data || {};
|
||||
|
||||
yield dispatch( STORE_NAME ).setTodos( todos );
|
||||
yield dispatch( STORE_NAME ).setDismissedTodos( dismissedTodos );
|
||||
yield dispatch( STORE_NAME ).setIsReady( true );
|
||||
|
||||
} catch ( e ) {
|
||||
console.error( 'Resolver error:', e );
|
||||
yield dispatch( STORE_NAME ).setIsReady( false );
|
||||
|
|
|
@ -22,16 +22,16 @@ export const transientData = ( state ) => {
|
|||
};
|
||||
|
||||
export const getTodos = ( state ) => {
|
||||
// Access todos directly from state first
|
||||
const todos = state?.todos || persistentData( state ).todos || EMPTY_ARR;
|
||||
return todos;
|
||||
const todos = state?.todos || persistentData( state ).todos;
|
||||
return todos || EMPTY_ARR;
|
||||
};
|
||||
|
||||
export const getDismissedTodos = ( state ) => {
|
||||
// Access dismissed todos directly from state first
|
||||
const dismissedTodos =
|
||||
state?.dismissedTodos ||
|
||||
persistentData( state ).dismissedTodos ||
|
||||
EMPTY_ARR;
|
||||
return dismissedTodos;
|
||||
const dismissed =
|
||||
state?.dismissedTodos || persistentData( state ).dismissedTodos;
|
||||
return dismissed || EMPTY_ARR;
|
||||
};
|
||||
|
||||
export const getCompletedTodos = ( state ) => {
|
||||
return state?.completedTodos || EMPTY_ARR; // Only look at root state, not persistent data
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
PaymentHooks,
|
||||
SettingsHooks,
|
||||
StylingHooks,
|
||||
TodosHooks,
|
||||
} from '../data';
|
||||
|
||||
export const useSaveSettings = () => {
|
||||
|
@ -13,6 +14,7 @@ export const useSaveSettings = () => {
|
|||
const { persist: persistPayment } = PaymentHooks.useStore();
|
||||
const { persist: persistSettings } = SettingsHooks.useStore();
|
||||
const { persist: persistStyling } = StylingHooks.useStore();
|
||||
const { persist: persistTodos } = TodosHooks.useStore();
|
||||
|
||||
const persistAll = useCallback( () => {
|
||||
withActivity(
|
||||
|
@ -30,7 +32,14 @@ export const useSaveSettings = () => {
|
|||
'Save styling details',
|
||||
persistStyling
|
||||
);
|
||||
}, [ persistPayment, persistSettings, persistStyling, withActivity ] );
|
||||
withActivity( 'persist-todos', 'Save todos state', persistTodos );
|
||||
}, [
|
||||
persistPayment,
|
||||
persistSettings,
|
||||
persistStyling,
|
||||
persistTodos,
|
||||
withActivity,
|
||||
] );
|
||||
|
||||
return { persistAll };
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@ use WooCommerce\PayPalCommerce\Settings\Endpoint\LoginLinkRestEndpoint;
|
|||
use WooCommerce\PayPalCommerce\Settings\Endpoint\OnboardingRestEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Settings\Endpoint\PaymentRestEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Settings\Endpoint\RefreshFeatureStatusEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Settings\Endpoint\ResetDismissedTodosEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Settings\Endpoint\WebhookSettingsEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Settings\Endpoint\SettingsRestEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Settings\Endpoint\StylingRestEndpoint;
|
||||
|
@ -239,7 +240,8 @@ return array(
|
|||
'settings.rest.todos' => static function ( ContainerInterface $container ) : TodosRestEndpoint {
|
||||
return new TodosRestEndpoint(
|
||||
$container->get( 'settings.data.todos' ),
|
||||
$container->get( 'settings.data.definition.todos' )
|
||||
$container->get( 'settings.data.definition.todos' ),
|
||||
$container->get( 'settings.rest.settings' )
|
||||
);
|
||||
},
|
||||
'settings.data.todos' => static function ( ContainerInterface $container ) : TodosModel {
|
||||
|
@ -255,13 +257,25 @@ return array(
|
|||
$container->has( 'axo.eligible' ) && $container->get( 'axo.eligible' ),
|
||||
$container->has( 'card-fields.eligible' ) && $container->get( 'card-fields.eligible' ),
|
||||
$container->has( 'paylater-configurator.is-available' ) && $container->get( 'paylater-configurator.is-available' ),
|
||||
$container->has( 'wc-subscriptions.helper' ) && $container->get( 'wc-subscriptions.helper' )->plugin_is_active(),
|
||||
$container->has( 'wc-subscriptions.helper' ) && $container->get( 'wc-subscriptions.helper' )->plugin_is_active() && empty(
|
||||
wc_get_products(
|
||||
array(
|
||||
'type' => 'subscription',
|
||||
'limit' => 1,
|
||||
)
|
||||
)
|
||||
),
|
||||
$container->has( 'apple-pay-domain.eligible' ) && $container->get( 'apple-pay-domain.eligible' ),
|
||||
true,
|
||||
$container->has( 'applepay.eligible' ) && $container->get( 'applepay.eligible' ),
|
||||
$container->has( 'googlepay.eligible' ) && $container->get( 'googlepay.eligible' ),
|
||||
$container->has( 'googlepay.eligible' ) &&
|
||||
$container->get( 'googlepay.eligible' ) &&
|
||||
( $container->get( 'googlepay.available' ) && ! $container->get( 'googlepay.is_referral' ) ),
|
||||
$container->has( 'paylater-messaging.available' ) && $container->get( 'paylater-messaging.available' ),
|
||||
$container->has( 'button.basic-checkout.enabled' ) && ! $container->get( 'button.basic-checkout.enabled' )
|
||||
);
|
||||
},
|
||||
'settings.rest.reset_dismissed_todos' => static function( ContainerInterface $container ): ResetDismissedTodosEndpoint {
|
||||
return new ResetDismissedTodosEndpoint();
|
||||
},
|
||||
);
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
/**
|
||||
* REST endpoint to reset dismissed things to do items.
|
||||
*
|
||||
* Provides endpoint for resetting all dismissed todos
|
||||
* via WP REST API route.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Settings\Endpoint;
|
||||
|
||||
use WP_REST_Server;
|
||||
use WP_REST_Response;
|
||||
use WP_REST_Request;
|
||||
|
||||
/**
|
||||
* Class ResetDismissedTodosEndpoint
|
||||
*
|
||||
* Handles REST API endpoint for resetting dismissed things to do items.
|
||||
*/
|
||||
class ResetDismissedTodosEndpoint extends RestEndpoint {
|
||||
/**
|
||||
* The base path for this REST controller.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rest_base = 'reset-dismissed-todos';
|
||||
|
||||
/**
|
||||
* Registers the REST API route for resetting todos.
|
||||
*/
|
||||
public function register_routes(): void {
|
||||
/**
|
||||
* POST wc/v3/wc_paypal/reset-dismissed-todos
|
||||
*/
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'reset_dismissed_todos' ),
|
||||
'permission_callback' => array( $this, 'check_permission' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all dismissed todos.
|
||||
*
|
||||
* @param WP_REST_Request $request The request instance.
|
||||
* @return WP_REST_Response The response containing reset status.
|
||||
*/
|
||||
public function reset_dismissed_todos( WP_REST_Request $request ): WP_REST_Response {
|
||||
$settings = get_option( 'ppcp-settings', array() );
|
||||
|
||||
$settings['dismissedTodos'] = array();
|
||||
$update_result = update_option( 'ppcp-settings', $settings );
|
||||
|
||||
if ( ! $update_result ) {
|
||||
return $this->return_error( __( 'Failed to reset dismissed todos.', 'woocommerce-paypal-payments' ) );
|
||||
}
|
||||
|
||||
return $this->return_success(
|
||||
array(
|
||||
'message' => __( 'Dismissed todos reset successfully.', 'woocommerce-paypal-payments' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,9 @@
|
|||
/**
|
||||
* REST endpoint to manage the things to do items.
|
||||
*
|
||||
* Provides endpoints for retrieving, updating, and resetting todos
|
||||
* via WP REST API routes.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Settings\Endpoint
|
||||
*/
|
||||
|
||||
|
@ -9,8 +12,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Settings\Endpoint;
|
||||
|
||||
use WP_REST_Response;
|
||||
use WP_REST_Server;
|
||||
use WP_REST_Response;
|
||||
use WP_REST_Request;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\TodosModel;
|
||||
use WooCommerce\PayPalCommerce\Settings\Data\Definition\TodosDefinition;
|
||||
|
||||
|
@ -44,55 +48,104 @@ class TodosRestEndpoint extends RestEndpoint {
|
|||
protected TodosDefinition $todos_definition;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* The settings endpoint instance.
|
||||
*
|
||||
* @param TodosModel $todos The todos model instance.
|
||||
* @param TodosDefinition $todos_definition The todos definition instance.
|
||||
* @var SettingsRestEndpoint
|
||||
*/
|
||||
protected SettingsRestEndpoint $settings;
|
||||
|
||||
/**
|
||||
* TodosRestEndpoint constructor.
|
||||
*
|
||||
* @param TodosModel $todos The todos model instance.
|
||||
* @param TodosDefinition $todos_definition The todos definition instance.
|
||||
* @param SettingsRestEndpoint $settings The settings endpoint instance.
|
||||
*/
|
||||
public function __construct(
|
||||
TodosModel $todos,
|
||||
TodosDefinition $todos_definition
|
||||
TodosDefinition $todos_definition,
|
||||
SettingsRestEndpoint $settings
|
||||
) {
|
||||
$this->todos = $todos;
|
||||
$this->todos_definition = $todos_definition;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure REST API routes.
|
||||
* Registers the REST API routes for todos management.
|
||||
*/
|
||||
public function register_routes(): void {
|
||||
/**
|
||||
* GET wc/v3/wc_paypal/todos
|
||||
* POST wc/v3/wc_paypal/todos
|
||||
*/
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base,
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_todos' ),
|
||||
'permission_callback' => array( $this, 'check_permission' ),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'get_todos' ),
|
||||
'permission_callback' => array( $this, 'check_permission' ),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::EDITABLE,
|
||||
'callback' => array( $this, 'update_todos' ),
|
||||
'permission_callback' => array( $this, 'check_permission' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all eligible todo items.
|
||||
* Retrieves the current todos.
|
||||
*
|
||||
* @return WP_REST_Response The response containing eligible todo items.
|
||||
* @return WP_REST_Response The response containing todos data.
|
||||
*/
|
||||
public function get_todos(): WP_REST_Response {
|
||||
$completion_states = $this->todos->get();
|
||||
$todos = array();
|
||||
$settings = get_option( 'ppcp-settings', array() );
|
||||
$dismissed_ids = $settings['dismissedTodos'] ?? array();
|
||||
|
||||
$todos = array();
|
||||
foreach ( $this->todos_definition->get() as $id => $todo ) {
|
||||
if ( $todo['isEligible']() ) {
|
||||
$todos[] = array_merge(
|
||||
array(
|
||||
'id' => $id,
|
||||
'isCompleted' => $completion_states[ $id ] ?? false,
|
||||
),
|
||||
array( 'id' => $id ),
|
||||
array_diff_key( $todo, array( 'isEligible' => true ) )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->return_success( $todos );
|
||||
return $this->return_success(
|
||||
array(
|
||||
'todos' => $todos,
|
||||
'dismissedTodos' => $dismissed_ids,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the todos with provided data.
|
||||
*
|
||||
* @param WP_REST_Request $request The request instance containing todo updates.
|
||||
* @return WP_REST_Response The response containing updated todos or error details.
|
||||
*/
|
||||
public function update_todos( WP_REST_Request $request ): WP_REST_Response {
|
||||
$data = $request->get_json_params();
|
||||
$settings = get_option( 'ppcp-settings', array() );
|
||||
|
||||
if ( isset( $data['dismissedTodos'] ) ) {
|
||||
$settings['dismissedTodos'] = array_unique(
|
||||
is_array( $data['dismissedTodos'] ) ? $data['dismissedTodos'] : array()
|
||||
);
|
||||
|
||||
$update_result = update_option( 'ppcp-settings', $settings );
|
||||
|
||||
if ( $update_result ) {
|
||||
return $this->return_success( $settings );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->return_success( $data );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,6 +238,7 @@ class SettingsModule implements ServiceModule, ExecutableModule {
|
|||
'settings' => $container->get( 'settings.rest.settings' ),
|
||||
'styling' => $container->get( 'settings.rest.styling' ),
|
||||
'todos' => $container->get( 'settings.rest.todos' ),
|
||||
'reset_dismissed_todos' => $container->get( 'settings.rest.reset_dismissed_todos' ),
|
||||
);
|
||||
|
||||
foreach ( $endpoints as $endpoint ) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue