Skip to content

Commit e36dc18

Browse files
committed
feat: video player and embed block integration
1 parent 6a503f2 commit e36dc18

File tree

11 files changed

+1097
-0
lines changed

11 files changed

+1097
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { BlockControls, InspectorControls } from '@wordpress/block-editor';
2+
import {
3+
Button,
4+
PanelBody,
5+
Placeholder,
6+
SelectControl,
7+
ToolbarButton,
8+
ToolbarGroup,
9+
Notice
10+
} from '@wordpress/components';
11+
import { useDispatch } from '@wordpress/data';
12+
import { useEffect, useMemo, useState } from '@wordpress/element';
13+
import { edit } from '@wordpress/icons';
14+
import { logo } from '../common/icons';
15+
16+
import { isOptimoleURL } from '../common/utils';
17+
18+
const Edit = ( props ) => {
19+
const { replaceBlock } = useDispatch( 'core/block-editor' );
20+
const { attributes, setAttributes, isSelected } = props;
21+
22+
const [ loading, setLoading ] = useState( false );
23+
const [ editing, setEditing ] = useState( false );
24+
const [ url, setUrl ] = useState( attributes.url );
25+
const [ error, setError ] = useState( false );
26+
27+
const onUrlChange = ( value ) => {
28+
setUrl( value );
29+
};
30+
31+
const onAspectRatioChange = ( value ) => {
32+
setAttributes({ aspectRatio: value });
33+
};
34+
35+
const onEdit = () => {
36+
setEditing( true );
37+
};
38+
39+
const onSave = ( event ) => {
40+
event.preventDefault();
41+
setError( false );
42+
43+
44+
const url = event.target.url.value;
45+
46+
isOptimoleURL( url ).then( ( valid ) => {
47+
if ( valid ) {
48+
setAttributes({ url });
49+
setEditing( false );
50+
return;
51+
}
52+
53+
setError( true );
54+
});
55+
56+
};
57+
58+
useEffect( () => {
59+
isOptimoleURL( attributes.url ).then( ( valid ) => {
60+
61+
if ( ! valid ) {
62+
setEditing( true );
63+
setError( true );
64+
}
65+
});
66+
}, [ attributes.url ]);
67+
68+
const aspectRatioOptions = useMemo( () => {
69+
return OMVideoPlayerBlock.aspectRatioOptions.map( ( value ) => ({
70+
label: value,
71+
value: value.replace( ':', '/' )
72+
}) );
73+
}, []);
74+
75+
const style = useMemo( () => {
76+
77+
78+
const style = {
79+
'--om-primary-color': attributes.primaryColor,
80+
'--om-aspect-ratio': attributes.aspectRatio
81+
};
82+
83+
if ( ! isSelected ) {
84+
style.pointerEvents = 'none';
85+
}
86+
87+
return style;
88+
}, [ attributes.aspectRatio, attributes.primaryColor, isSelected ]);
89+
90+
return (
91+
<>
92+
<BlockControls>
93+
<ToolbarGroup>
94+
<ToolbarButton
95+
icon={edit}
96+
label={OMVideoPlayerBlock.editLabel}
97+
onClick={onEdit}
98+
/>
99+
</ToolbarGroup>
100+
</BlockControls>
101+
102+
<InspectorControls>
103+
<PanelBody title={OMVideoPlayerBlock.blockTitle} initialOpen={true}>
104+
105+
<SelectControl
106+
value={attributes.aspectRatio}
107+
label={OMVideoPlayerBlock.aspectRatioLabel}
108+
options={aspectRatioOptions}
109+
onChange={onAspectRatioChange}
110+
/>
111+
112+
</PanelBody>
113+
</InspectorControls>
114+
{! editing && <optimole-video-player video-src={props.attributes.url} style={style}></optimole-video-player>}
115+
{editing && <Placeholder
116+
label={OMVideoPlayerBlock.urlLabel}
117+
className="wp-block-embed"
118+
>
119+
<form onSubmit={onSave}>
120+
<input type="url" id="url" defaultValue={attributes.url} className="wp-block-embed__placeholder-input" onChange={onUrlChange} />
121+
<Button isPrimary type="submit">Save</Button>
122+
</form>
123+
124+
{error && <Notice status="error" isDismissible={false}><span dangerouslySetInnerHTML={{ __html: OMVideoPlayerBlock.invalidUrlError }}/></Notice>}
125+
126+
</Placeholder>
127+
}
128+
</>
129+
);
130+
};
131+
132+
export default {
133+
icon: logo,
134+
title: OMVideoPlayerBlock.blockTitle,
135+
description: OMVideoPlayerBlock.blockDescription,
136+
137+
supports: {
138+
align: true,
139+
inserter: false,
140+
spacing: {
141+
margin: true
142+
}
143+
},
144+
category: 'embed',
145+
attributes: {
146+
url: {
147+
type: 'string',
148+
default: ''
149+
},
150+
aspectRatio: {
151+
type: 'string',
152+
default: '16/9'
153+
},
154+
primaryColor: {
155+
type: 'string',
156+
default: '#577BF9'
157+
}
158+
},
159+
edit: Edit,
160+
save: ( props ) => {
161+
return null;
162+
}
163+
};

assets/src/video-player/block/hoc.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { createHigherOrderComponent } from '@wordpress/compose';
2+
import { useDispatch } from '@wordpress/data';
3+
import { useEffect, useState } from '@wordpress/element';
4+
import { createBlock } from '@wordpress/blocks';
5+
import { isOptimoleURL } from '../common/utils';
6+
const BlockReplacer = ({ clientIDToReplace, url }) => {
7+
const { replaceBlock } = useDispatch( 'core/block-editor' );
8+
9+
useEffect( ()=> {
10+
const block = createBlock( 'optimole/video-player', {
11+
url: url,
12+
editing: isOptimoleURL
13+
});
14+
15+
replaceBlock( clientIDToReplace, block );
16+
}, [ clientIDToReplace ]);
17+
18+
return null;
19+
};
20+
21+
/**
22+
* Higher order component to check URLs and auto-detect custom embeds
23+
*/
24+
export const withCustomEmbedURLDetection = createHigherOrderComponent( ( BlockEdit ) => {
25+
return ( props ) => {
26+
const [ validOptimoleURL, setValidOptimoleURL ] = useState( false );
27+
28+
const { attributes } = props;
29+
const { url } = attributes;
30+
31+
useEffect( ()=> {
32+
isOptimoleURL( props.attributes.url ).then( ( valid ) => {
33+
setValidOptimoleURL( valid );
34+
});
35+
}, [ props.attributes.url ]);
36+
37+
if ( 'core/embed' !== props.name ) {
38+
return <BlockEdit {...props} />;
39+
}
40+
41+
42+
if ( url && validOptimoleURL ) {
43+
return <BlockReplacer clientIDToReplace={props.clientId} url={attributes.url} />;
44+
}
45+
46+
return <BlockEdit {...props} />;
47+
};
48+
}, 'withCustomEmbedURLDetection' );

assets/src/video-player/common/icons.js

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const isOptimoleURL = async( url ) => {
2+
const customEmbedRegexPattern = new RegExp( `^https?:\\/\\/(?:www\\.)?${OMVideoPlayerBlock.domain}\\/.*$` );
3+
4+
if ( ! customEmbedRegexPattern.test( url ) ) {
5+
return false;
6+
}
7+
8+
try {
9+
const response = await fetch( url, { method: 'HEAD' });
10+
if ( response.ok ) {
11+
const contentType = response.headers.get( 'content-type' );
12+
13+
return contentType.includes( 'video' );
14+
}
15+
} catch ( error ) {
16+
return false;
17+
}
18+
};

assets/src/video-player/editor.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/** global OMVideoPlayerBlock */
2+
import { registerBlockType, registerBlockVariation } from '@wordpress/blocks';
3+
import { addFilter } from '@wordpress/hooks';
4+
5+
import { withCustomEmbedURLDetection } from './block/hoc';
6+
import VideoPlayerBlock from './block/VideoPlayerBlock';
7+
import { logo } from './common/icons';
8+
9+
import './web-components/player';
10+
11+
/**
12+
* Register the video player block.
13+
*/
14+
registerBlockType( 'optimole/video-player', VideoPlayerBlock );
15+
16+
/**
17+
* Register the embed video player block variation.
18+
*/
19+
registerBlockVariation( 'core/embed', {
20+
name: 'optimole',
21+
attributes: {
22+
providerNameSlug: 'optimole'
23+
},
24+
icon: logo,
25+
title: OMVideoPlayerBlock.blockTitle,
26+
description: OMVideoPlayerBlock.blockDescription,
27+
category: 'embed',
28+
isActive: ( blockAttributes, variationAttributes ) => {
29+
return 'optimole' === blockAttributes.providerNameSlug;
30+
},
31+
patterns: [
32+
`^https?:\\/\\/(?:www\\.)?${OMVideoPlayerBlock.domain}\\/.*$`
33+
]
34+
});
35+
36+
/**
37+
* Filter to add custom embed detection to the editor.
38+
*/
39+
addFilter(
40+
'editor.BlockEdit',
41+
'custom-embed/with-custom-embed-detection',
42+
withCustomEmbedURLDetection
43+
);

assets/src/video-player/frontend.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import './style.scss';
2+
import './web-components/player';

0 commit comments

Comments
 (0)