Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web Story Embed block for Gutenberg #1249

Merged
merged 91 commits into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
cdbe022
Fix webpack build issue
swissspidy Apr 16, 2020
e0a5d96
Add skeleton for web story embed block
swissspidy Apr 16, 2020
c9972f1
Merge branch 'master' into add/gutenberg-block
swissspidy Apr 21, 2020
5145fdf
Merge branch 'master' into add/gutenberg-block
swissspidy Apr 22, 2020
2bcb3d6
MVP embed controller
swissspidy Apr 22, 2020
62a60d7
Merge branch 'master' into add/gutenberg-block
swissspidy Apr 22, 2020
cf06668
fix lock file
swissspidy Apr 22, 2020
582f871
some minor fixes
swissspidy Apr 22, 2020
45bbdad
Merge branch 'master' into add/gutenberg-block
swissspidy Apr 22, 2020
13ae5a9
Merge branch 'master' into add/gutenberg-block
swissspidy Apr 23, 2020
37c55c4
Merge branch 'master' into add/gutenberg-block
swissspidy Apr 28, 2020
a3e5e47
Merge branch 'master' into add/gutenberg-block
swissspidy Apr 28, 2020
e68e379
Add ability to resize the player using slider controls
swissspidy Apr 28, 2020
7c6f341
ESLint: detect useSelect hooks
swissspidy Apr 28, 2020
01690da
Add ability to override poster via sidebar
swissspidy Apr 28, 2020
97e1848
PHP fixes
swissspidy Apr 28, 2020
59e715a
Add simple php unit test
swissspidy Apr 29, 2020
d062673
Rename files
swissspidy Apr 29, 2020
c8b2647
Prettier
swissspidy Apr 29, 2020
0599502
Prop type fix
swissspidy Apr 29, 2020
d2067dd
Mock window.matchMedia in tests
swissspidy Apr 29, 2020
b9ca1bc
Update ESLint config
swissspidy Apr 29, 2020
e83d54a
Add snapshot tests for StoryPlayer component
swissspidy Apr 29, 2020
3fbf394
Unregister blocks before running tsts
swissspidy Apr 29, 2020
a955640
Add test for save function
swissspidy Apr 29, 2020
681be4f
Simplify resizable
swissspidy Apr 29, 2020
58f0933
stylelint fix
swissspidy Apr 29, 2020
858e700
Merge branch 'master' into add/gutenberg-block
swissspidy Apr 29, 2020
7a3a2d3
Add Embed_Controller tests
swissspidy Apr 29, 2020
232206f
Use assertEqualSetsWithIndex
swissspidy Apr 29, 2020
9b50882
Collect code coverage for new block
swissspidy Apr 29, 2020
b646f18
Add ability to resize the player using input fields in sidebar
swissspidy Apr 29, 2020
48be75d
Allow amp-story-player in KSES
swissspidy Apr 29, 2020
c01e3c3
Some UX tweaks around failed embeds
swissspidy Apr 29, 2020
f127baf
More fixes
swissspidy Apr 29, 2020
40c79b6
Return early in save function if there is no url or title
swissspidy Apr 29, 2020
855ed7e
Merge branch 'master' into add/gutenberg-block
swissspidy May 4, 2020
c2e382e
Use optional chaining
swissspidy May 4, 2020
ad2fcc9
Make title editable via sidebar
swissspidy May 4, 2020
175a0c9
Make block partially dynamic
swissspidy May 4, 2020
c8f3ea8
Merge branch 'master' into add/gutenberg-block
swissspidy May 4, 2020
38ce34f
Cast to string
swissspidy May 4, 2020
0e729b2
Merge branch 'master' into add/gutenberg-block
spacedmonkey May 6, 2020
a1fa48a
Merge branch 'master' into add/gutenberg-block
swissspidy May 7, 2020
7ccdafa
fix package.json
swissspidy May 7, 2020
e244003
Make sure scripts are properly loaded in editor
swissspidy May 7, 2020
e93f8ab
Fix bugs for fresh blocks
swissspidy May 7, 2020
0556a59
Add tests for EmbedPlaceholder
swissspidy May 7, 2020
fd9cba9
Remove redundant vars
swissspidy May 7, 2020
3df1a43
Optimize data fetching
swissspidy May 7, 2020
f67a1d0
Update Embed_Controller
swissspidy May 7, 2020
4388768
type -> url
swissspidy May 7, 2020
b3b38f5
Merge branch 'master' into add/gutenberg-block
swissspidy May 7, 2020
2177285
Define block attribures server-side
swissspidy May 7, 2020
22217a2
Add todo
swissspidy May 7, 2020
64331a9
tests
swissspidy May 7, 2020
25e4f8a
Merge branch 'master' into add/gutenberg-block
swissspidy May 7, 2020
feb843d
Merge branch 'master' into add/gutenberg-block
swissspidy May 8, 2020
cb74ded
Merge branch 'master' into add/gutenberg-block
swissspidy May 11, 2020
6a32dd3
Fix php tests
swissspidy May 11, 2020
1a9af2f
Catch empty urls early
swissspidy May 11, 2020
381e392
improve logic for local posts
swissspidy May 11, 2020
c4a8bf0
further fixes and improvements
swissspidy May 11, 2020
c0b716b
some phpstan fixes
swissspidy May 11, 2020
b2eb2b8
Fix remaining issue
swissspidy May 11, 2020
19c2ea8
Merge branch 'master' into add/gutenberg-block
swissspidy May 14, 2020
5b7fdb3
correct default state
swissspidy May 14, 2020
bc72367
Merge branch 'master' into add/gutenberg-block
swissspidy May 14, 2020
88e4aa0
Merge branch 'master' into add/gutenberg-block
swissspidy May 15, 2020
3fd301b
Merge branch 'master' into add/gutenberg-block
swissspidy May 16, 2020
b1eb8dc
Ensure player is loaded properly in editor
swissspidy May 16, 2020
e6c93f1
Optimize site query
swissspidy May 16, 2020
01b869a
Merge branch 'master' into add/gutenberg-block
swissspidy May 18, 2020
95def9d
Display a simple link in an RSS feed context
swissspidy May 18, 2020
dcb4499
Add comments to css file
swissspidy May 18, 2020
e867876
Display poster preview in sidebar
swissspidy May 18, 2020
a6fbdfd
Make min/max width/height more reasonable
swissspidy May 18, 2020
23668b1
Tweak placeholder css
swissspidy May 18, 2020
8da8642
Use async/await
swissspidy May 18, 2020
d0eadb3
Merge branch 'master' into add/gutenberg-block
swissspidy May 18, 2020
0604473
Do not cache errors
swissspidy May 18, 2020
96dea32
Add more tests
swissspidy May 18, 2020
8ab889c
Fix type prop
swissspidy May 18, 2020
eef37d5
Use only `absint`
swissspidy May 18, 2020
3d8efbf
Merge branch 'master' into add/gutenberg-block
swissspidy May 18, 2020
e4a588a
Add overlay to player in preview
swissspidy May 18, 2020
5abc569
Merge branch 'master' into add/gutenberg-block
swissspidy May 18, 2020
5b4231d
Merge branch 'master' into add/gutenberg-block
swissspidy May 19, 2020
97d5d3c
More render_block tests
swissspidy May 19, 2020
9d49bed
Remove unneded check
swissspidy May 19, 2020
1bd543a
Merge branch 'master' into add/gutenberg-block
swissspidy May 19, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
"require-await": "error",
"rest-spread-spacing": ["error", "never"],
"react/prop-types": "error",
"react-hooks/exhaustive-deps": ["error", {"additionalHooks": "useBatchingCallback"}],
"react-hooks/exhaustive-deps": ["error", {"additionalHooks": "useBatchingCallback|useSelect"}],
"react/jsx-fragments": "error",
"react/jsx-no-literals": "error",
"react/jsx-no-useless-fragment": "error",
Expand Down
27 changes: 27 additions & 0 deletions assets/src/story-embed-block/block/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "web-stories/embed",
"category": "embed",
"attributes": {
swissspidy marked this conversation as resolved.
Show resolved Hide resolved
"url": {
"type": "url"
},
"title": {
"type": "string"
},
"poster": {
"type": "string"
},
"width": {
"type": "number",
"default": 360
},
"height": {
"type": "number",
"default": 600
},
"align": {
"type": "string",
"default": "none"
}
}
}
78 changes: 78 additions & 0 deletions assets/src/story-embed-block/block/edit.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* Loading Indicator */

.wp-block-web-stories-embed.is-loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1em;
min-height: 200px;
text-align: center;
background: #f8f9f9;
}

.wp-block-web-stories-embed.is-loading p {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
font-size: 13px;
}

/* Placeholder */

.wp-block-web-stories-embed .components-placeholder__learn-more {
margin-top: 1em;
}

/* Actual Edit Component */

.wp-block-web-stories-embed
.components-resizable-box__container
amp-story-player {
width: inherit;
height: inherit;
}

/* Embed Preview Component */

.web-stories-embed-preview {
position: relative;
width: inherit;
height: inherit;
}

.web-stories-embed-preview-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
}

/* Block Inspector */

.web-stories-embed-poster-label {
display: block !important;
}

.web-stories-embed-size-control {
margin-bottom: 1em;
}

.web-stories-embed-size-control__row {
display: flex;
justify-content: space-between;
}

.web-stories-embed-size-control__width input,
.web-stories-embed-size-control__height input {
line-height: 1.25;
}

.web-stories-embed-size-control__width {
margin-right: 5px;
}

.web-stories-embed-size-control__height {
margin-left: 5px;
}
253 changes: 253 additions & 0 deletions assets/src/story-embed-block/block/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* External dependencies
*/
import PropTypes from 'prop-types';

/**
* WordPress dependencies
*/
import { useCallback, useState, useEffect, useRef } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { __ } from '@wordpress/i18n';
import { ResizableBox } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import EmbedControls from './embedControls';
import EmbedLoadinng from './embedLoading';
import EmbedPlaceholder from './embedPlaceholder';
import EmbedPreview from './embedPreview';
import { icon } from './index.js';
import './edit.css';

const MIN_SIZE = 20;

function StoryEmbedEdit({ attributes, setAttributes, className, isSelected }) {
const {
url: outerURL,
width = 360,
height = 600,
align = 'none',
poster,
title,
} = attributes;

const [editingURL, setEditingURL] = useState(false);
const [localURL, setLocalURL] = useState(outerURL);
const [isFetchingData, setIsFetchingData] = useState(false);
const [storyData, setStoryData] = useState({});
const [cannotEmbed, setCannotEmbed] = useState(false);

const showLoadingIndicator = isFetchingData;
const showPlaceholder = !localURL || !outerURL || editingURL || cannotEmbed;

const ref = useRef();

useEffect(() => {
setLocalURL(outerURL);
}, [outerURL]);

useEffect(() => {
if (ref.current && global.ampStoryPlayer) {
const player = new global.ampStoryPlayer(global, ref.current);
player.load();
}
}, [showLoadingIndicator, showPlaceholder]);

const fetchStoryData = useCallback(
async (url) => {
if (!url) {
return;
}

try {
swissspidy marked this conversation as resolved.
Show resolved Hide resolved
setIsFetchingData(true);
const urlObj = new URL(url);

const data = await apiFetch({
path: `web-stories/v1/embed?url=${urlObj.toString()}`,
});

setCannotEmbed(!data?.title);
setStoryData(data);
setAttributes({
url: localURL,
});
} catch (err) {
// Only care about errors from apiFetch
if (!(err instanceof TypeError)) {
setStoryData(err);
}

setCannotEmbed(true);
} finally {
setIsFetchingData(false);
}
},
[setAttributes, localURL]
);

useEffect(() => {
if (storyData?.title || storyData?.poster) {
setAttributes({
title: storyData?.title,
poster: storyData?.poster,
});
}
}, [outerURL, storyData, setAttributes]);

const onSubmit = useCallback(
(event) => {
if (event) {
event.preventDefault();
}

setEditingURL(false);
setCannotEmbed(false);
if (localURL !== outerURL) {
fetchStoryData(localURL);
}
},
[localURL, outerURL, fetchStoryData]
);

const switchBackToURLInput = useCallback(() => {
setEditingURL(true);
}, []);

const { isRTL, maxWidth } = useSelect((select) => {
const { getSettings } = select('core/block-editor');
const settings = getSettings();
return {
isRTL: settings.isRTL,
maxWidth: settings.maxWidth,
};
}, []);

const { toggleSelection } = useDispatch('core/block-editor');

if (showLoadingIndicator) {
return <EmbedLoadinng />;
}

const onResizeStart = () => toggleSelection(false);
const onResizeStop = () => toggleSelection(true);

const label = __('Web Story URL', 'web-stories');

if (showPlaceholder) {
return (
<EmbedPlaceholder
icon={icon}
label={label}
value={localURL}
onSubmit={onSubmit}
onChange={(event) => setLocalURL(event.target.value)}
cannotEmbed={cannotEmbed}
errorMessage={storyData?.message}
/>
);
}

const ratio = width / height;
const minWidth = width < height ? MIN_SIZE : MIN_SIZE * ratio;
const minHeight = height < width ? MIN_SIZE : MIN_SIZE / ratio;

const showRightHandle =
align === 'center' ||
align === 'none' ||
(align === 'right' && isRTL) ||
(align === 'left' && !isRTL);

const showLeftHandle =
align === 'center' ||
(align === 'left' && isRTL) ||
(align === 'right' && !isRTL);

return (
<>
<EmbedControls
switchBackToURLInput={switchBackToURLInput}
poster={poster}
title={title}
setAttributes={setAttributes}
width={width}
height={height}
minWidth={Math.ceil(minWidth)}
maxWidth={Math.floor(maxWidth)}
minHeight={Math.floor(minHeight)}
maxHeight={Math.ceil(maxWidth / ratio)}
/>
<div className={`${className} align${align}`}>
<ResizableBox
showHandle={isSelected}
size={{
width,
height,
}}
minWidth={minWidth}
maxWidth={maxWidth}
minHeight={minHeight}
maxHeight={maxWidth / ratio}
lockAspectRatio
enable={{
top: false,
right: showRightHandle,
bottom: true,
left: showLeftHandle,
}}
onResizeStart={onResizeStart}
onResizeStop={(event, direction, elt, delta) => {
onResizeStop();
setAttributes({
width: parseInt(width + delta.width),
height: parseInt(height + delta.height),
});
}}
>
<EmbedPreview
url={outerURL}
title={title}
poster={poster}
ref={ref}
isSelected={isSelected}
/>
</ResizableBox>
</div>
</>
);
}

StoryEmbedEdit.propTypes = {
attributes: PropTypes.shape({
url: PropTypes.string,
title: PropTypes.string,
poster: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
align: PropTypes.string,
}),
setAttributes: PropTypes.func.isRequired,
className: PropTypes.string.isRequired,
isSelected: PropTypes.bool,
};

export default StoryEmbedEdit;
Loading