copyable embed tag

This commit is contained in:
jessop 2019-10-29 12:23:56 -04:00 committed by Sean Yesmunt
parent 0ad4adceca
commit c0640012a9
9 changed files with 118 additions and 77 deletions

View file

@ -1,8 +1,8 @@
// @flow
import * as ICONS from 'constants/icons';
import * as React from 'react';
import { FormField } from 'component/common/form';
import Button from 'component/button';
import React, { useRef } from 'react';
type Props = {
copyable: string,
@ -11,58 +11,48 @@ type Props = {
label?: string,
};
export default class CopyableText extends React.PureComponent<Props> {
constructor() {
super();
export default function CopyableText(props: Props) {
const { copyable, doToast, snackMessage, label } = props;
this.input = React.createRef();
(this: any).onFocus = this.onFocus.bind(this);
(this: any).copyToClipboard = this.copyToClipboard.bind(this);
}
const input = useRef();
input: { current: React.ElementRef<any> };
copyToClipboard() {
const topRef = this.input.current;
function copyToClipboard() {
const topRef = input.current;
if (topRef && topRef.input && topRef.input.current) {
topRef.input.current.select();
}
document.execCommand('copy');
}
onFocus() {
function onFocus() {
// We have to go a layer deep since the input is inside the form component
const topRef = this.input.current;
const topRef = input.current;
if (topRef && topRef.input && topRef.input.current) {
topRef.input.current.select();
}
}
render() {
const { copyable, doToast, snackMessage, label } = this.props;
return (
<FormField
type="text"
className="form-field--copyable"
readOnly
label={label}
value={copyable || ''}
ref={this.input}
onFocus={this.onFocus}
inputButton={
<Button
button="inverse"
icon={ICONS.COPY}
onClick={() => {
this.copyToClipboard();
doToast({
message: snackMessage || __('Text copied'),
});
}}
/>
}
/>
);
}
return (
<FormField
type="text"
className="form-field--copyable"
readOnly
label={label}
value={copyable || ''}
ref={input}
onFocus={onFocus}
inputButton={
<Button
button="inverse"
icon={ICONS.COPY}
onClick={() => {
copyToClipboard();
doToast({
message: snackMessage || __('Text copied'),
});
}}
/>
}
/>
);
}

View file

@ -0,0 +1,10 @@
import { connect } from 'react-redux';
import { doToast } from 'lbry-redux';
import EmbedArea from './view';
export default connect(
null,
{
doToast,
}
)(EmbedArea);

View file

@ -0,0 +1,63 @@
// @flow
import * as ICONS from 'constants/icons';
import { FormField } from 'component/common/form';
import Button from 'component/button';
import React, { useRef } from 'react';
import { generateStreamUrl } from 'util/lbrytv';
import { LBRY_TV_API } from 'config';
type Props = {
copyable: string,
snackMessage: ?string,
doToast: ({ message: string }) => void,
label?: string,
claim: Claim,
};
export default function EmbedArea(props: Props) {
const { doToast, snackMessage, label, claim } = props;
const { claim_id: claimId, name } = claim;
const input = useRef();
const streamUrl = generateStreamUrl(name, claimId, LBRY_TV_API);
let embedText = `<iframe width="560" height="315" src="${streamUrl}" allowfullscreen></iframe>`;
function copyToClipboard() {
const topRef = input.current;
if (topRef && topRef.input && topRef.input.current) {
topRef.input.current.select();
}
document.execCommand('copy');
}
function onFocus() {
// We have to go a layer deep since the input is inside the form component
const topRef = input.current;
if (topRef && topRef.input && topRef.input.current) {
topRef.input.current.select();
}
}
return (
<fieldset-section>
<FormField
type="textarea"
className="form-field--copyable"
label={label}
value={embedText || ''}
ref={input}
readOnly
onFocus={onFocus}
/>
<div className="card__actions card__actions--center">
<Button
icon={ICONS.COPY}
button="inverse"
onClick={() => {
copyToClipboard();
doToast({ message: snackMessage || 'Embed link copied' });
}}
/>
</div>
</fieldset-section>
);
}

View file

@ -17,7 +17,7 @@ export default function ShareButton(props: Props) {
button="alt"
icon={ICONS.SHARE}
label={__('Share')}
onClick={() => doOpenModal(MODALS.SOCIAL_SHARE, { uri, speechShareable: true, isChannel: true })}
onClick={() => doOpenModal(MODALS.SOCIAL_SHARE, { uri, webShareable: true, isChannel: true })}
/>
);
}

View file

@ -3,12 +3,12 @@ import * as ICONS from 'constants/icons';
import React from 'react';
import Button from 'component/button';
import CopyableText from 'component/copyableText';
import { DOMAIN } from 'config';
import EmbedArea from 'component/embedArea';
type Props = {
claim: Claim,
onDone: () => void,
speechShareable: boolean,
webShareable: boolean,
isChannel: boolean,
};
@ -28,42 +28,18 @@ class SocialShare extends React.PureComponent<Props> {
render() {
const { claim } = this.props;
const { canonical_url: canonicalUrl, permanent_url: permanentUrl } = claim;
const { speechShareable, onDone } = this.props;
const lbryTvPrefix = `${DOMAIN}/`;
const { webShareable, onDone } = this.props;
const OPEN_URL = 'https://open.lbry.com/';
const lbryUrl = canonicalUrl ? canonicalUrl.split('lbry://')[1] : permanentUrl.split('lbry://')[1];
const lbryWebUrl = lbryUrl.replace(/#/g, ':');
const encodedLbryURL: string = `${OPEN_URL}${encodeURIComponent(lbryWebUrl)}`;
const lbryURL: string = `${OPEN_URL}${lbryWebUrl}`;
const encodedLbryTvUrl = `${lbryTvPrefix}${encodeURIComponent(lbryWebUrl)}`;
const lbryTvUrl = `${lbryTvPrefix}${lbryWebUrl}`;
const shareOnFb = __('Share on Facebook');
const shareOnTwitter = __('Share On Twitter');
return (
<React.Fragment>
{speechShareable && (
<div>
<CopyableText label={__('Web link')} copyable={lbryTvUrl} />
<div className="card__actions card__actions--center">
<Button
icon={ICONS.FACEBOOK}
button="link"
description={shareOnFb}
href={`https://facebook.com/sharer/sharer.php?u=${encodedLbryTvUrl}`}
/>
<Button
icon={ICONS.TWITTER}
button="link"
description={shareOnTwitter}
href={`https://twitter.com/intent/tweet?text=${encodedLbryTvUrl}`}
/>
<Button icon={ICONS.WEB} button="link" description={__('View on lbry.tv')} href={`${lbryTvUrl}`} />
</div>
</div>
)}
<CopyableText label={__('LBRY App Link')} copyable={lbryURL} noSnackbar />
<div className="card__actions card__actions--center">
<Button
@ -79,6 +55,8 @@ class SocialShare extends React.PureComponent<Props> {
href={`https://twitter.com/intent/tweet?text=${encodedLbryURL}`}
/>
</div>
{webShareable && <EmbedArea label={__('Embedded')} claim={claim} noSnackbar />}
{!webShareable && <p className={'help'}>{__('Paid content cannot be embedded')}</p>}
<div className="card__actions">
<Button button="link" label={__('Done')} onClick={onDone} />
</div>

View file

@ -6,16 +6,16 @@ import SocialShare from 'component/socialShare';
type Props = {
closeModal: () => void,
uri: string,
speechShareable: boolean,
webShareable: boolean,
isChannel: boolean,
};
class ModalSocialShare extends React.PureComponent<Props> {
render() {
const { closeModal, uri, speechShareable, isChannel } = this.props;
const { closeModal, uri, webShareable, isChannel } = this.props;
return (
<Modal isOpen onAborted={closeModal} type="custom" title={__('Share')}>
<SocialShare uri={uri} onDone={closeModal} speechShareable={speechShareable} isChannel={isChannel} />
<SocialShare uri={uri} onDone={closeModal} webShareable={webShareable} isChannel={isChannel} />
</Modal>
);
}

View file

@ -118,7 +118,7 @@ class FilePage extends React.Component<Props> {
const { signing_channel: signingChannel } = claim;
const channelName = signingChannel && signingChannel.name;
const isRewardContent = (rewardedContentClaimIds || []).includes(claim.claim_id);
const speechShareable =
const webShareable =
costInfo && costInfo.cost === 0 && contentType && ['video', 'image', 'audio'].includes(contentType.split('/')[0]);
// We want to use the short form uri for editing
// This is what the user is used to seeing, they don't care about the claim id
@ -203,7 +203,7 @@ class FilePage extends React.Component<Props> {
button="alt"
icon={icons.SHARE}
label={__('Share')}
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, speechShareable })}
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, webShareable })}
/>
</div>

View file

@ -1,5 +1,5 @@
function generateStreamUrl(claimName, claimId) {
const prefix = process.env.SDK_API_URL;
function generateStreamUrl(claimName, claimId, apiUrl) {
const prefix = process.env.SDK_API_URL || apiUrl;
return `${prefix}/content/claims/${claimName}/${claimId}/stream`;
}

View file

@ -828,8 +828,8 @@
"To enable this feature, check 'Save Password' the next time you start the app.": "To enable this feature, check 'Save Password' the next time you start the app.",
"An email address is required to sync your account.": "An email address is required to sync your account.",
"Sign Out": "Sign Out",
"Follow more tags": "Follow more tags",
"Portuguese": "Portuguese",
"Follow more tags": "Follow more tags",
"Sign In to LBRY": "Sign In to LBRY",
"Check Your Email": "Check Your Email",
"sign in": "sign in",