Skip to content

Commit

Permalink
refactor: simplify logic
Browse files Browse the repository at this point in the history
  • Loading branch information
glaubersilva committed Feb 21, 2025
1 parent a4e6fce commit d5f21cc
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {CampaignsRowActions} from './CampaignsRowActions';
import styles from './CampaignsListTable.module.scss';
import {GiveCampaignsListTable} from './types';
import CreateCampaignModal from '../CreateCampaignModal';
import {useState} from 'react';
import {useEffect, useRef, useState} from 'react';
import MergeCampaignModal from '../MergeCampaign/Modal';

declare const window: {
Expand All @@ -18,7 +18,7 @@ declare const window: {
*
* @unreleased
*/
const autoOpenModal = () => {
const autoOpenCreateCampaignModal = () => {
const queryParams = new URLSearchParams(window.location.search);
const newParam = queryParams.get('new');

Expand Down Expand Up @@ -66,31 +66,47 @@ const filters: Array<FilterConfig> = [
},
];

const bulkActions: Array<BulkActionsConfig> = [
{
label: __('Merge', 'give'),
value: 'merge',
type: 'custom',
confirm: (selected, names) => {
if (window.history.state === 'merge-campaigns-modal-closed') {
return null;
}
export default function CampaignsListTable() {
const [isCreateCampaignModalOpen, setCreateCampaignModalOpen] = useState<boolean>(autoOpenCreateCampaignModal());
const [isMergeCampaignsModalOpen, setMergeCampaignsModalOpen] = useState<boolean>(false);
const [campaignsToMerge, setCampaignsToMerge] = useState({
selected: [] as (string | number)[],
names: [] as string[],
});

const urlParams = new URLSearchParams(window.location.search);
urlParams.set('action', 'merge');
window.history.replaceState(
{selected: selected, names: names},
__('Merge Campaigns', 'give'),
`${window.location.pathname}?${urlParams.toString()}`
);
/**
* useRef is used to store the latest value of `isMergeCampaignsModalOpen` without causing re-renders.
* This ensures that the most up-to-date state is accessible synchronously, even during asynchronous updates or closures,
* such as when the `confirm` function is executed. Without useRef, the stale value of `isMergeCampaignsModalOpen` captured by
* the closure could lead to incorrect behavior, like reopening the modal unnecessarily.
*/
const isMergeCampaignsModalOpenRef = useRef<boolean>(isMergeCampaignsModalOpen);
useEffect(() => {
isMergeCampaignsModalOpenRef.current = isMergeCampaignsModalOpen;
}, [isMergeCampaignsModalOpen]);

return null;
const bulkActions: Array<BulkActionsConfig> = [
{
label: __('Merge', 'give'),
value: 'merge',
type: 'custom',
confirm: (selected, names) => {
if (!isMergeCampaignsModalOpenRef.current) {
/**
* This timeout prevents this error from being thrown in the browser console:
* Warning: Cannot update a component (`CampaignsListTable`) while rendering a different component (`ListTablePage`).
*
* @see https://github.com/facebook/react/issues/18178#issuecomment-595846312
*/
setTimeout(() => {
setCampaignsToMerge({selected, names});
setMergeCampaignsModalOpen(true);
}, 0);
}
return null;
},
},
},
];

export default function CampaignsListTable() {
const [isOpen, setOpen] = useState<boolean>(autoOpenModal());
];

/**
* Displays a blank slate for the Campaigns table.
Expand All @@ -109,7 +125,10 @@ export default function CampaignsListTable() {
{__('Don’t worry, let’s help you setup your first campaign.', 'give')}
</p>
<p>
<a onClick={() => setOpen(true)} className={`button button-primary ${styles.button}`}>
<a
onClick={() => setCreateCampaignModalOpen(true)}
className={`button button-primary ${styles.button}`}
>
{__('Create campaign', 'give')}
</a>
</p>
Expand All @@ -129,8 +148,12 @@ export default function CampaignsListTable() {
rowActions={CampaignsRowActions}
listTableBlankSlate={ListTableBlankSlate()}
>
<CreateCampaignModal isOpen={isOpen} setOpen={setOpen} />
<MergeCampaignModal />
<CreateCampaignModal isOpen={isCreateCampaignModalOpen} setOpen={setCreateCampaignModalOpen} />
<MergeCampaignModal
isOpen={isMergeCampaignsModalOpen}
setOpen={setMergeCampaignsModalOpen}
campaigns={campaignsToMerge}
/>
</ListTablePage>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
export interface MergeCampaignModalProps {
isOpen: boolean;
setOpen: (response?: any) => void;
campaigns: {
selected: (string | number)[];
names: string[];
};
}

export interface MergeCampaignFormProps {
isOpen: boolean;
handleClose: (response?: any) => void;
title: string;
campaigns: {
selected: string[];
selected: (string | number)[];
names: string[];
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,102 +1,17 @@
import {useEffect, useState} from 'react';
import {__} from '@wordpress/i18n';
import MergeCampaignsForm from './../Form';

/**
* Remove the "action" query parameter from the current URL
*
* @unreleased
*/
const removeActionParam = () => {
const queryParams = new URLSearchParams(window.location.search);
const actionParam = queryParams.get('action');

if (actionParam) {
queryParams.delete('action');
window.history.replaceState(
'merge-campaigns-modal-closed',
'',
`${window.location.pathname}?${queryParams.toString()}`
);
}
};

/**
* Auto open modal if the URL has the query parameter action as "merge"
*
* @unreleased
*/
const autoOpenModal = () => {
const queryParams = new URLSearchParams(window.location.search);
const actionParam = queryParams.get('action');

if (actionParam && !window.history.state) {
removeActionParam();
return false;
}

return actionParam === 'merge';
};
import {MergeCampaignModalProps} from '@givewp/campaigns/admin/components/MergeCampaign/Form/types';

/**
* Create Campaign Modal component
*
* @unreleased
*/
export default function MergeCampaignModal() {
const [isOpen, setOpen] = useState<boolean>(autoOpenModal());
export default function MergeCampaignModal({isOpen, setOpen, campaigns}: MergeCampaignModalProps) {
const closeModal = () => {
removeActionParam();
setOpen(false);
};

const campaigns = {
selected: window.history.state?.selected || [],
names: window.history.state?.names || [],
};

useEffect(() => {
// Override pushState and replaceState to trigger a custom event
const originalPushState = window.history.pushState;
const originalReplaceState = window.history.replaceState;

window.history.pushState = function (...args) {
originalPushState.apply(window.history, args);
window.dispatchEvent(new Event('urlChange'));
};

window.history.replaceState = function (...args) {
originalReplaceState.apply(window.history, args);
window.dispatchEvent(new Event('urlChange'));
};

// Add listeners for "popstate" and the custom "urlChange" event
const handleQueryParamsChange = () => {
/**
* This timeout prevents this error from being thrown in the browser console:
* Warning: Cannot update a component (`MergeCampaignModal`) while rendering a different component (`ListTablePage`).
*
* @see https://github.com/facebook/react/issues/18178#issuecomment-595846312
*/
const initializeModalState = () => {
setOpen(autoOpenModal());
};
setTimeout(initializeModalState, 0);
};
window.addEventListener('popstate', handleQueryParamsChange);
window.addEventListener('urlChange', handleQueryParamsChange);

// Remove listeners when the component unmounts
return () => {
window.removeEventListener('popstate', handleQueryParamsChange);
window.removeEventListener('urlChange', handleQueryParamsChange);

// Restore the original pushState and replaceState functions
window.history.pushState = originalPushState;
window.history.replaceState = originalReplaceState;
};
}, []);

return (
<>
<MergeCampaignsForm
Expand Down
1 change: 0 additions & 1 deletion src/Views/Components/ListTable/ListTablePage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ export default function ListTablePage({
if (selected.length) {
setModalContent({...bulkActions[actionIndex]});
if ('custom' === bulkActions[actionIndex].type) {
window.history.replaceState(null, '');
modalContent?.confirm(selected, names);
} else {
dialog.current.show();
Expand Down

0 comments on commit d5f21cc

Please sign in to comment.