Merge branch 'next-embeds' into 'develop'

Embeds

See merge request soapbox-pub/soapbox-fe!1202
api-accept
Alex Gleason 2 years ago
commit 80d5e26c8a

@ -381,11 +381,14 @@ class StatusActionBar extends ImmutablePureComponent<IStatusActionBar, IStatusAc
action: this.handleCopy,
icon: require('@tabler/icons/icons/link.svg'),
});
// menu.push({
// text: intl.formatMessage(messages.embed),
// action: this.handleEmbed,
// icon: require('feather-icons/dist/icons/link-2.svg'),
// });
if (features.embeds) {
menu.push({
text: intl.formatMessage(messages.embed),
action: this.handleEmbed,
icon: require('@tabler/icons/icons/share.svg'),
});
}
}
if (!me) {

@ -11,7 +11,7 @@ const messages = defineMessages({
hidePassword: { id: 'input.password.hide_password', defaultMessage: 'Hide password' },
});
interface IInput extends Pick<React.InputHTMLAttributes<HTMLInputElement>, 'maxLength' | 'onChange' | 'type' | 'autoComplete' | 'autoCorrect' | 'autoCapitalize' | 'required' | 'disabled'> {
interface IInput extends Pick<React.InputHTMLAttributes<HTMLInputElement>, 'maxLength' | 'onChange' | 'type' | 'autoComplete' | 'autoCorrect' | 'autoCapitalize' | 'required' | 'disabled' | 'onClick' | 'readOnly'> {
/** Put the cursor into the input on mount. */
autoFocus?: boolean,
/** The initial text in the input. */

@ -369,11 +369,14 @@ class ActionBar extends React.PureComponent<IActionBar, IActionBarState> {
action: this.handleCopy,
icon: require('@tabler/icons/icons/link.svg'),
});
// menu.push({
// text: intl.formatMessage(messages.embed),
// action: this.handleEmbed,
// icon: require('feather-icons/dist/icons/link-2.svg'),
// });
if (features.embeds) {
menu.push({
text: intl.formatMessage(messages.embed),
action: this.handleEmbed,
icon: require('@tabler/icons/icons/share.svg'),
});
}
}
if (me) {

@ -1,88 +0,0 @@
import axios from 'axios';
import PropTypes from 'prop-types';
import React from 'react';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { FormattedMessage, injectIntl } from 'react-intl';
export default @injectIntl
class EmbedModal extends ImmutablePureComponent {
static propTypes = {
url: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
onError: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
}
state = {
loading: false,
oembed: null,
};
componentDidMount() {
const { url } = this.props;
this.setState({ loading: true });
axios.post('/api/web/embed', { url }).then(res => {
this.setState({ loading: false, oembed: res.data });
const iframeDocument = this.iframe.contentWindow.document;
iframeDocument.open();
iframeDocument.write(res.data.html);
iframeDocument.close();
iframeDocument.body.style.margin = 0;
this.iframe.width = iframeDocument.body.scrollWidth;
this.iframe.height = iframeDocument.body.scrollHeight;
}).catch(error => {
this.props.onError(error);
});
}
setIframeRef = c => {
this.iframe = c;
}
handleTextareaClick = (e) => {
e.target.select();
}
render() {
const { oembed } = this.state;
return (
<div className='modal-root__modal embed-modal'>
<h4><FormattedMessage id='status.embed' defaultMessage='Embed' /></h4>
<div className='embed-modal__container'>
<p className='hint'>
<FormattedMessage id='embed.instructions' defaultMessage='Embed this post on your website by copying the code below.' />
</p>
<input
type='text'
className='embed-modal__html'
readOnly
value={oembed && oembed.html || ''}
onClick={this.handleTextareaClick}
/>
<p className='hint'>
<FormattedMessage id='embed.preview' defaultMessage='Here is what it will look like:' />
</p>
<iframe
className='embed-modal__iframe'
frameBorder='0'
ref={this.setIframeRef}
sandbox='allow-same-origin'
title='preview'
/>
</div>
</div>
);
}
}

@ -0,0 +1,81 @@
import React, { useState, useEffect, useRef } from 'react';
import { FormattedMessage } from 'react-intl';
import api from 'soapbox/api';
import { Modal, Stack, Text, Input } from 'soapbox/components/ui';
import { useAppDispatch } from 'soapbox/hooks';
const fetchEmbed = (url: string) => {
return (dispatch: any, getState: any) => {
return api(getState).get('/api/oembed', { params: { url } });
};
};
interface IEmbedModal {
url: string,
onError: (error: any) => void,
}
const EmbedModal: React.FC<IEmbedModal> = ({ url, onError }) => {
const dispatch = useAppDispatch();
const iframe = useRef<HTMLIFrameElement>(null);
const [oembed, setOembed] = useState<any>(null);
useEffect(() => {
dispatch(fetchEmbed(url)).then(({ data }) => {
if (!iframe.current?.contentWindow) return;
setOembed(data);
const iframeDocument = iframe.current.contentWindow.document;
iframeDocument.open();
iframeDocument.write(data.html);
iframeDocument.close();
const innerFrame = iframeDocument.querySelector('iframe');
iframeDocument.body.style.margin = '0';
if (innerFrame) {
innerFrame.width = '100%';
}
}).catch(error => {
onError(error);
});
}, [!!iframe.current]);
const handleInputClick: React.MouseEventHandler<HTMLInputElement> = (e) => {
e.currentTarget.select();
};
return (
<Modal title={<FormattedMessage id='status.embed' defaultMessage='Embed' />}>
<Stack space={4}>
<Stack>
<Text theme='muted' size='sm'>
<FormattedMessage id='embed.instructions' defaultMessage='Embed this post on your website by copying the code below.' />
</Text>
<Input
type='text'
readOnly
value={oembed?.html || ''}
onClick={handleInputClick}
/>
</Stack>
<iframe
className='inline-flex rounded-xl overflow-hidden max-w-full'
frameBorder='0'
ref={iframe}
sandbox='allow-same-origin'
title='preview'
/>
</Stack>
</Modal>
);
};
export default EmbedModal;

@ -211,6 +211,12 @@ const getInstanceFeatures = (instance: Instance) => {
*/
emailList: features.includes('email_list'),
/**
* Ability to embed posts on external sites.
* @see GET /api/oembed
*/
embeds: v.software === MASTODON,
/**
* Ability to add emoji reactions to a status.
* @see PUT /api/v1/pleroma/statuses/:id/reactions/:emoji

Loading…
Cancel
Save