Initial
This commit is contained in:
237
src/components/DraggableItem.js
Normal file
237
src/components/DraggableItem.js
Normal file
@ -0,0 +1,237 @@
|
||||
import { useDrag, useDrop } from 'react-dnd';
|
||||
import { DateTimePicker,SelectControl, TextControl, __experimentalNumberControl as NumberControl } from '@wordpress/components';
|
||||
import { useState, useEffect, useCallback } from '@wordpress/element';
|
||||
|
||||
const DraggableItem = ({ id, index, moveItem, postType, children }) => {
|
||||
// Drag-and-drop hooks with memoization
|
||||
const [{ isDragging }, drag] = useDrag(
|
||||
useCallback(() => ({
|
||||
type: 'rule',
|
||||
item: { id, index },
|
||||
collect: (monitor) => ({
|
||||
isDragging: !!monitor.isDragging(),
|
||||
}),
|
||||
}), [id, index])
|
||||
);
|
||||
|
||||
const [, drop] = useDrop(
|
||||
useCallback(() => ({
|
||||
accept: 'rule',
|
||||
hover: (item) => {
|
||||
if (item.index !== index) {
|
||||
moveItem(item.index, index);
|
||||
item.index = index; // Keep track of the moved index
|
||||
}
|
||||
},
|
||||
}), [index, moveItem])
|
||||
);
|
||||
|
||||
// State management
|
||||
const [ruleType, setRuleType] = useState('post');
|
||||
const [lockType, setLockType] = useState('has_subscription');
|
||||
const [operator, setOperator] = useState('=');
|
||||
const [metaKey, setMetaKey] = useState('');
|
||||
const [roles, setRoles] = useState([]); // State to store user roles
|
||||
const [taxonomies, setTaxonomies] = useState([]); // State to store taxonomies
|
||||
const [terms, setTerms] = useState([]); // State to store terms of a selected taxonomy
|
||||
const [postContentLengthAmount, setPostContentLengthAmount] = useState(0);
|
||||
const [postContentLengthUnit, setPostContentLengthUnit] = useState('characters');
|
||||
const [postContentLengthOperator, setPostContentLengthOperator] = useState('=');
|
||||
const [postDate,setPostDate] = useState('')
|
||||
const [postDateOperator, setPostDateOperator] = useState('=');
|
||||
const [metaKeyOperator, setMetaKeyOperator] = useState('=');
|
||||
const [metaValue, setMetaValue] = useState('');
|
||||
|
||||
|
||||
// Fetch user roles from the WordPress REST API
|
||||
useEffect(() => {
|
||||
if (lockType === 'has_role') {
|
||||
fetch('/localcontentpro/wp-json/lcp-paywall/v1/user-roles') // Your custom REST API endpoint
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
setRoles(data.map(role => ({ label: role.name, value: role.key })));
|
||||
})
|
||||
.catch((error) => console.error('Error fetching roles:', error));
|
||||
}
|
||||
}, [lockType]); // Fetch only when lockType is 'has_role'
|
||||
|
||||
// Fetch taxonomies for the current post type
|
||||
useEffect(() => {
|
||||
if ((lockType === 'in_term' || lockType === 'not_in_term') && postType) {
|
||||
fetch(`/localcontentpro/wp-json/lcp-paywall/v1/taxonomies?post_type=${postType}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const taxonomyOptions = Object.keys(data).map(taxonomy => ({
|
||||
label: data[taxonomy].name,
|
||||
value: taxonomy,
|
||||
}));
|
||||
setTaxonomies(taxonomyOptions);
|
||||
})
|
||||
.catch(error => console.error('Error fetching taxonomies:', error));
|
||||
}
|
||||
}, [lockType, postType]); // Fetch taxonomies when lockType is 'in_term' and postType changes
|
||||
|
||||
// Fetch terms when lockType is in_term or not_in_term and a taxonomy is selected
|
||||
useEffect(() => {
|
||||
if ((lockType === 'in_term' || lockType === 'not_in_term') && metaKey) {
|
||||
fetch(`/localcontentpro/wp-json/lcp-paywall/v1/terms?taxonomy=${metaKey}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const termOptions = data.map(term => ({
|
||||
label: term.name,
|
||||
value: term.id,
|
||||
}));
|
||||
setTerms(termOptions);
|
||||
})
|
||||
.catch(error => console.error('Error fetching terms:', error));
|
||||
}
|
||||
}, [lockType, metaKey]); // Fetch terms when lockType is 'in_term' or 'not_in_term' and metaKey (taxonomy) is selected
|
||||
|
||||
const postLockTypeOptions = [
|
||||
{ value: 'post_content_length', label: 'Post Content Length' },
|
||||
{ value: 'post_in', label: 'Post In' },
|
||||
{value: 'post_not_in', label: 'Post Not In' },
|
||||
{ value: 'meta_value', label: 'Meta Value' },
|
||||
{ value: 'in_term', label: 'In Term' },
|
||||
{ value: 'not_in_term', label: 'Not In Term' },
|
||||
{ value: 'older_than', label: 'Older Than' },
|
||||
{ value: 'newer_than', label: 'Newer Than' },
|
||||
{ value: 'author_in', label: 'Author In' },
|
||||
{ value: 'author_not_in', label: 'Author Not In' },
|
||||
{ value: 'post_date', label: 'Post Date' }
|
||||
];
|
||||
|
||||
const userLockTypeOptions = [
|
||||
{ label: 'Has Subscription', value: 'has_subscription' },
|
||||
{ label: 'Is Logged In', value: 'is_logged_in' },
|
||||
{ label: 'Role In', value: 'role_in' },
|
||||
{ label: 'Role Not In', value: 'role_not_in' },
|
||||
{ label: 'Meta Value', value: 'meta_value' }
|
||||
];
|
||||
|
||||
const userDeviceLockTypeOptions = [
|
||||
{ label: 'Browser In', value: 'browser_in' },
|
||||
{ label: 'Browser Not In', value: 'browser_not_in' },
|
||||
{ label: 'Operating System In', value: 'os_in' },
|
||||
{ label: 'Operating System Not In', value: 'os_not_in' }
|
||||
];
|
||||
|
||||
const comparisonOperators = [
|
||||
{ label: '=', value: '=' },
|
||||
{ label: '!=', value: '!=' },
|
||||
{ label: '>', value: '>' },
|
||||
{ label: '<', value: '<' },
|
||||
{ label: '>=', value: '>=' },
|
||||
{ label: '<=', value: '<=' },
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={(node) => drag(drop(node))}
|
||||
className={`lcp-paywall-draggable-item ${isDragging ? 'is-dragging' : ''}`}
|
||||
style={{ opacity: isDragging ? 0.5 : 1 }}
|
||||
>
|
||||
<div className="lcp-paywall-rule-controls">
|
||||
<SelectControl
|
||||
value={ruleType}
|
||||
options={[
|
||||
{ label: 'Current Post', value: 'post' },
|
||||
{ label: 'Current User', value: 'user' },
|
||||
{ label: 'Current User Device', value: 'user_device' },
|
||||
]}
|
||||
onChange={setRuleType}
|
||||
/>
|
||||
|
||||
<SelectControl
|
||||
value={lockType}
|
||||
options={
|
||||
ruleType === 'post' ? postLockTypeOptions : ruleType === 'user' ? userLockTypeOptions : userDeviceLockTypeOptions
|
||||
}
|
||||
onChange={setLockType}
|
||||
/>
|
||||
|
||||
{lockType === 'meta_value' && (
|
||||
<>
|
||||
<TextControl
|
||||
value={metaKey}
|
||||
onChange={setMetaKey}
|
||||
placeholder="Meta Key"
|
||||
/>
|
||||
<SelectControl
|
||||
value={metaKeyOperator}
|
||||
options={comparisonOperators}
|
||||
onChange={setMetaKeyOperator}
|
||||
/>
|
||||
|
||||
|
||||
<TextControl
|
||||
value={metaValue}
|
||||
onChange={setMetaValue}
|
||||
placeholder="Meta Value"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{lockType === 'has_role' && (
|
||||
<SelectControl
|
||||
value={metaKey}
|
||||
options={roles}
|
||||
onChange={setMetaKey}
|
||||
/>
|
||||
)}
|
||||
|
||||
{(lockType === 'in_term' || lockType === 'not_in_term') && (
|
||||
<>
|
||||
<SelectControl
|
||||
value={metaKey}
|
||||
options={taxonomies}
|
||||
onChange={setMetaKey}
|
||||
/>
|
||||
<SelectControl
|
||||
value={metaKey}
|
||||
options={terms}
|
||||
onChange={setMetaKey}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{lockType === 'post_content_length' && (
|
||||
<>
|
||||
<SelectControl
|
||||
value={postContentLengthOperator}
|
||||
options={comparisonOperators}
|
||||
onChange={setPostContentLengthOperator}
|
||||
/>
|
||||
<NumberControl
|
||||
value={postContentLengthAmount}
|
||||
onChange={setPostContentLengthAmount}
|
||||
/>
|
||||
<SelectControl
|
||||
value={postContentLengthUnit}
|
||||
options={[
|
||||
{ label: 'Characters', value: 'characters' },
|
||||
{ label: 'Words', value: 'words' } ]}
|
||||
onChange={setPostContentLengthUnit}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{lockType === 'post_date' && (
|
||||
<>
|
||||
<SelectControl
|
||||
value={postDateOperator}
|
||||
options={comparisonOperators}
|
||||
onChange={setPostDateOperator}
|
||||
/>
|
||||
<DateTimePicker
|
||||
value={postDate}
|
||||
onChange={setPostDate}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DraggableItem;
|
||||
120
src/components/PostTypeContainer.js
Normal file
120
src/components/PostTypeContainer.js
Normal file
@ -0,0 +1,120 @@
|
||||
import { useDrop } from 'react-dnd';
|
||||
import { Button, ToggleControl, __experimentalNumberControl as NumberControl, SelectControl } from '@wordpress/components';
|
||||
import { useState, useEffect, useCallback } from '@wordpress/element';
|
||||
import DraggableItem from './DraggableItem';
|
||||
|
||||
const PostTypeContainer = ({ postType }) => {
|
||||
const [rules, setRules] = useState([{ id: Date.now() }]);
|
||||
const [lockDefault, setLockDefault] = useState(false);
|
||||
const [enableMeter, setEnableMeter] = useState(false);
|
||||
const [meterAmount, setMeterAmount] = useState(0);
|
||||
const [meterDuration, setMeterDuration] = useState(1);
|
||||
const [meterCookieUnits, setMeterCookieUnits] = useState('hour');
|
||||
const [totalSeconds, setTotalSeconds] = useState(3600);
|
||||
|
||||
const [{ isOver }, drop] = useDrop(() => ({
|
||||
accept: 'rule',
|
||||
drop: () => ({ postType: postType.name }),
|
||||
collect: (monitor) => ({
|
||||
isOver: !!monitor.isOver(),
|
||||
}),
|
||||
}));
|
||||
|
||||
const moveItem = useCallback((dragIndex, hoverIndex) => {
|
||||
setRules((prevRules) => {
|
||||
const newRules = [...prevRules];
|
||||
const dragItem = newRules[dragIndex];
|
||||
|
||||
// Remove the dragged item
|
||||
newRules.splice(dragIndex, 1);
|
||||
// Insert it at the new position
|
||||
newRules.splice(hoverIndex, 0, dragItem);
|
||||
|
||||
return newRules;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const addRule = () => {
|
||||
setRules([...rules, { id: Date.now() }]);
|
||||
};
|
||||
|
||||
const calculateTotalSeconds = (duration, unit) => {
|
||||
const unitToSeconds = {
|
||||
second: 1,
|
||||
minute: 60,
|
||||
hour: 3600,
|
||||
day: 86400,
|
||||
week: 604800,
|
||||
};
|
||||
return duration * (unitToSeconds[unit] || 0);
|
||||
};
|
||||
|
||||
// Update total seconds whenever duration or unit changes
|
||||
useEffect(() => {
|
||||
setTotalSeconds(calculateTotalSeconds(meterDuration, meterCookieUnits));
|
||||
}, [meterDuration, meterCookieUnits]);
|
||||
|
||||
return (
|
||||
<div className={`lcp-paywall-post-type ${isOver ? 'is-over' : ''}`} ref={drop}>
|
||||
<h3>{postType.label}</h3>
|
||||
<ToggleControl
|
||||
label="Lock Posts By Default"
|
||||
checked={lockDefault}
|
||||
onChange={setLockDefault}
|
||||
/>
|
||||
<ToggleControl
|
||||
label="Enable Meter"
|
||||
checked={enableMeter}
|
||||
onChange={setEnableMeter}
|
||||
/>
|
||||
{enableMeter && (
|
||||
<>
|
||||
<NumberControl
|
||||
label="Meter Amount"
|
||||
value={meterAmount}
|
||||
onChange={setMeterAmount}
|
||||
/>
|
||||
<NumberControl
|
||||
label="Meter Cookie Duration"
|
||||
value={meterDuration}
|
||||
onChange={setMeterDuration}
|
||||
/>
|
||||
<SelectControl
|
||||
label="Meter Cookie Units"
|
||||
value={meterCookieUnits}
|
||||
options={[
|
||||
{ label: 'Minutes', value: 'minute' },
|
||||
{ label: 'Hours', value: 'hour' },
|
||||
{ label: 'Days', value: 'day' },
|
||||
{ label: 'Weeks', value: 'week' },
|
||||
]}
|
||||
onChange={setMeterCookieUnits}
|
||||
/>
|
||||
<p><strong>Total Seconds:</strong> {totalSeconds}</p>
|
||||
</>
|
||||
)}
|
||||
<div className="lcp-paywall-rules-container">
|
||||
{rules.map((rule, index) => (
|
||||
<DraggableItem
|
||||
key={rule.id}
|
||||
id={rule.id}
|
||||
index={index}
|
||||
moveItem={moveItem}
|
||||
postType={postType.name}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="lcp-paywall-add-rule">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={addRule}
|
||||
className="lcp-paywall-add-rule-button"
|
||||
>
|
||||
Add Rule
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostTypeContainer;
|
||||
24
src/index.js
Normal file
24
src/index.js
Normal file
@ -0,0 +1,24 @@
|
||||
import { render } from '@wordpress/element';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import PostTypeContainer from './components/PostTypeContainer';
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<div id="lcp-paywall-app">
|
||||
{window.lcpPaywallData.postTypes.map(postType => (
|
||||
<PostTypeContainer key={postType.name} postType={postType} />
|
||||
))}
|
||||
</div>
|
||||
</DndProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// Wait for DOM to be ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const container = document.getElementById('lcp-paywall-app-container');
|
||||
if (container) {
|
||||
render(<App />, container);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user