fix: Critical data structure and mutation bugs
## 🐛 Critical Fixes ### Issue 1: Toggling One Channel Affects Both **Problem:** Disabling email disabled both email and push **Root Cause:** Optimistic update with `onSettled` refetch caused race condition **Fix:** Removed optimistic update, use server response directly **Before:** ```ts onMutate: async () => { // Optimistic update queryClient.setQueryData(...) } onSettled: () => { // This refetch caused race condition queryClient.invalidateQueries(...) } ``` **After:** ```ts onSuccess: (data, variables) => { // Update cache with verified server response queryClient.setQueryData([...], (old) => old.map(channel => channel.id === variables.channelId ? { ...channel, enabled: data.enabled } : channel ) ); } ``` ### Issue 2: Events Cannot Be Enabled **Problem:** All event channels disabled and cannot be enabled **Root Cause:** Wrong data structure in `update_event()` **Before:** ```php $settings[$event_id][$channel_id] = [...]; // Saved as: { "order_placed": { "email": {...} } } ``` **After:** ```php $settings[$event_id]['channels'][$channel_id] = [...]; // Saves as: { "order_placed": { "channels": { "email": {...} } } } ``` ### Issue 3: POST Data Not Parsed **Problem:** Event updates not working **Root Cause:** Using `get_param()` instead of `get_json_params()` **Fix:** Changed to `get_json_params()` in `update_event()` ### What Was Fixed 1. ✅ Channel toggles work independently 2. ✅ No race conditions from optimistic updates 3. ✅ Event channel data structure matches get_events 4. ✅ Event toggles save correctly 5. ✅ POST data parsed properly 6. ✅ Boolean type enforcement ### Data Structure **Correct Structure:** ```php [ 'order_placed' => [ 'channels' => [ 'email' => ['enabled' => true, 'recipient' => 'admin'], 'push' => ['enabled' => false, 'recipient' => 'admin'] ] ] ] ``` --- **All toggles should now work correctly!** ✅
This commit is contained in:
@@ -47,38 +47,25 @@ export default function NotificationChannels() {
|
|||||||
// Toggle channel mutation
|
// Toggle channel mutation
|
||||||
const toggleChannelMutation = useMutation({
|
const toggleChannelMutation = useMutation({
|
||||||
mutationFn: async ({ channelId, enabled }: { channelId: string; enabled: boolean }) => {
|
mutationFn: async ({ channelId, enabled }: { channelId: string; enabled: boolean }) => {
|
||||||
return api.post('/notifications/channels/toggle', { channelId, enabled });
|
const response = await api.post('/notifications/channels/toggle', { channelId, enabled });
|
||||||
|
return response;
|
||||||
},
|
},
|
||||||
onMutate: async ({ channelId, enabled }) => {
|
onSuccess: (data, variables) => {
|
||||||
// Cancel outgoing refetches
|
// Update cache with server response
|
||||||
await queryClient.cancelQueries({ queryKey: ['notification-channels'] });
|
|
||||||
|
|
||||||
// Snapshot previous value
|
|
||||||
const previousChannels = queryClient.getQueryData(['notification-channels']);
|
|
||||||
|
|
||||||
// Optimistically update
|
|
||||||
queryClient.setQueryData(['notification-channels'], (old: any) => {
|
queryClient.setQueryData(['notification-channels'], (old: any) => {
|
||||||
if (!old) return old;
|
if (!old) return old;
|
||||||
return old.map((channel: any) =>
|
return old.map((channel: any) =>
|
||||||
channel.id === channelId ? { ...channel, enabled } : channel
|
channel.id === variables.channelId
|
||||||
|
? { ...channel, enabled: data.enabled }
|
||||||
|
: channel
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return { previousChannels };
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
toast.success(__('Channel updated'));
|
toast.success(__('Channel updated'));
|
||||||
},
|
},
|
||||||
onError: (error: any, variables, context: any) => {
|
onError: (error: any) => {
|
||||||
// Rollback on error
|
// Refetch on error to sync with server
|
||||||
if (context?.previousChannels) {
|
|
||||||
queryClient.setQueryData(['notification-channels'], context.previousChannels);
|
|
||||||
}
|
|
||||||
toast.error(error?.message || __('Failed to update channel'));
|
|
||||||
},
|
|
||||||
onSettled: () => {
|
|
||||||
// Refetch after mutation
|
|
||||||
queryClient.invalidateQueries({ queryKey: ['notification-channels'] });
|
queryClient.invalidateQueries({ queryKey: ['notification-channels'] });
|
||||||
|
toast.error(error?.message || __('Failed to update channel'));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -297,10 +297,11 @@ class NotificationsController {
|
|||||||
* @return WP_REST_Response|WP_Error
|
* @return WP_REST_Response|WP_Error
|
||||||
*/
|
*/
|
||||||
public function update_event(WP_REST_Request $request) {
|
public function update_event(WP_REST_Request $request) {
|
||||||
$event_id = $request->get_param('eventId');
|
$params = $request->get_json_params();
|
||||||
$channel_id = $request->get_param('channelId');
|
$event_id = isset($params['eventId']) ? $params['eventId'] : null;
|
||||||
$enabled = $request->get_param('enabled');
|
$channel_id = isset($params['channelId']) ? $params['channelId'] : null;
|
||||||
$recipient = $request->get_param('recipient');
|
$enabled = isset($params['enabled']) ? $params['enabled'] : null;
|
||||||
|
$recipient = isset($params['recipient']) ? $params['recipient'] : null;
|
||||||
|
|
||||||
if (empty($event_id) || empty($channel_id)) {
|
if (empty($event_id) || empty($channel_id)) {
|
||||||
return new WP_Error(
|
return new WP_Error(
|
||||||
@@ -315,16 +316,20 @@ class NotificationsController {
|
|||||||
|
|
||||||
// Update settings
|
// Update settings
|
||||||
if (!isset($settings[$event_id])) {
|
if (!isset($settings[$event_id])) {
|
||||||
$settings[$event_id] = [];
|
$settings[$event_id] = ['channels' => []];
|
||||||
}
|
}
|
||||||
|
|
||||||
$settings[$event_id][$channel_id] = [
|
if (!isset($settings[$event_id]['channels'])) {
|
||||||
'enabled' => $enabled,
|
$settings[$event_id]['channels'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings[$event_id]['channels'][$channel_id] = [
|
||||||
|
'enabled' => (bool) $enabled,
|
||||||
'recipient' => $recipient ?? 'admin',
|
'recipient' => $recipient ?? 'admin',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Save settings
|
// Save settings
|
||||||
update_option('woonoow_notification_settings', $settings);
|
update_option('woonoow_notification_settings', $settings, false);
|
||||||
|
|
||||||
// Fire action for addons to react
|
// Fire action for addons to react
|
||||||
do_action('woonoow_notification_event_updated', $event_id, $channel_id, $enabled, $recipient);
|
do_action('woonoow_notification_event_updated', $event_id, $channel_id, $enabled, $recipient);
|
||||||
|
|||||||
Reference in New Issue
Block a user