parent
aa0a76a121
commit
8fe9cfafbc
8 changed files with 190 additions and 18 deletions
|
@ -11,14 +11,16 @@ type Props = {
|
||||||
doToast: ({ message: string }) => void,
|
doToast: ({ message: string }) => void,
|
||||||
label?: string,
|
label?: string,
|
||||||
claim: Claim,
|
claim: Claim,
|
||||||
|
includeStartTime: boolean,
|
||||||
|
startTime: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function EmbedTextArea(props: Props) {
|
export default function EmbedTextArea(props: Props) {
|
||||||
const { doToast, snackMessage, label, claim } = props;
|
const { doToast, snackMessage, label, claim, includeStartTime, startTime } = props;
|
||||||
const { claim_id: claimId, name } = claim;
|
const { claim_id: claimId, name } = claim;
|
||||||
const input = useRef();
|
const input = useRef();
|
||||||
|
|
||||||
const streamUrl = generateEmbedUrl(name, claimId);
|
const streamUrl = generateEmbedUrl(name, claimId, includeStartTime, startTime);
|
||||||
let embedText = `<iframe width="560" height="315" src="${streamUrl}" allowfullscreen></iframe>`;
|
let embedText = `<iframe width="560" height="315" src="${streamUrl}" allowfullscreen></iframe>`;
|
||||||
|
|
||||||
function copyToClipboard() {
|
function copyToClipboard() {
|
||||||
|
@ -47,6 +49,7 @@ export default function EmbedTextArea(props: Props) {
|
||||||
value={embedText || ''}
|
value={embedText || ''}
|
||||||
ref={input}
|
ref={input}
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
|
readOnly
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
|
|
|
@ -2,12 +2,14 @@ import { connect } from 'react-redux';
|
||||||
import { makeSelectClaimForUri, makeSelectTitleForUri } from 'lbry-redux';
|
import { makeSelectClaimForUri, makeSelectTitleForUri } from 'lbry-redux';
|
||||||
import SocialShare from './view';
|
import SocialShare from './view';
|
||||||
import { selectUserInviteReferralCode, selectUser } from 'lbryinc';
|
import { selectUserInviteReferralCode, selectUser } from 'lbryinc';
|
||||||
|
import { makeSelectContentPositionForUri } from 'redux/selectors/content';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
referralCode: selectUserInviteReferralCode(state),
|
referralCode: selectUserInviteReferralCode(state),
|
||||||
user: selectUser(state),
|
user: selectUser(state),
|
||||||
title: makeSelectTitleForUri(props.uri)(state),
|
title: makeSelectTitleForUri(props.uri)(state),
|
||||||
|
position: makeSelectContentPositionForUri(props.uri)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select)(SocialShare);
|
export default connect(select)(SocialShare);
|
||||||
|
|
|
@ -6,6 +6,9 @@ import CopyableText from 'component/copyableText';
|
||||||
import EmbedTextArea from 'component/embedTextArea';
|
import EmbedTextArea from 'component/embedTextArea';
|
||||||
import { generateDownloadUrl } from 'util/lbrytv';
|
import { generateDownloadUrl } from 'util/lbrytv';
|
||||||
import useIsMobile from 'effects/use-is-mobile';
|
import useIsMobile from 'effects/use-is-mobile';
|
||||||
|
import { FormField } from 'component/common/form';
|
||||||
|
import { hmsToSeconds, secondsToHms } from 'util/time';
|
||||||
|
import { generateLbryUrl, generateLbryWebUrl, generateEncodedLbryURL, generateOpenDotLbryDotComUrl } from 'util/url';
|
||||||
|
|
||||||
const IOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
|
const IOS = !!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform);
|
||||||
const SUPPORTS_SHARE_API = typeof navigator.share !== 'undefined';
|
const SUPPORTS_SHARE_API = typeof navigator.share !== 'undefined';
|
||||||
|
@ -16,29 +19,56 @@ type Props = {
|
||||||
webShareable: boolean,
|
webShareable: boolean,
|
||||||
referralCode: string,
|
referralCode: string,
|
||||||
user: any,
|
user: any,
|
||||||
|
position: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
function SocialShare(props: Props) {
|
function SocialShare(props: Props) {
|
||||||
const { claim, title, referralCode, user, webShareable } = props;
|
const { claim, title, referralCode, user, webShareable, position } = props;
|
||||||
const [showEmbed, setShowEmbed] = React.useState(false);
|
const [showEmbed, setShowEmbed] = React.useState(false);
|
||||||
const [showExtra, setShowExtra] = React.useState(false);
|
const [showExtra, setShowExtra] = React.useState(false);
|
||||||
|
const [includeStartTime, setincludeStartTime]: [boolean, any] = React.useState(false);
|
||||||
|
const [startTime, setStartTime]: [string, any] = React.useState(secondsToHms(position));
|
||||||
|
const [startTimeSeconds, setStartTimeSeconds]: [number, any] = React.useState(Math.floor(position));
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
|
let canonicalUrl = 'lbry://';
|
||||||
|
let permanentUrl = 'lbry://';
|
||||||
|
let name = '';
|
||||||
|
let claimId = '';
|
||||||
|
|
||||||
|
if (claim) {
|
||||||
|
canonicalUrl = claim.canonical_url;
|
||||||
|
permanentUrl = claim.permanent_url;
|
||||||
|
name = claim.name;
|
||||||
|
claimId = claim.claim_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isChannel = claim.value_type === 'channel';
|
||||||
|
const rewardsApproved = user && user.is_reward_approved;
|
||||||
|
const OPEN_URL = 'https://open.lbry.com/';
|
||||||
|
const lbryUrl: string = generateLbryUrl(canonicalUrl, permanentUrl);
|
||||||
|
const lbryWebUrl: string = generateLbryWebUrl(lbryUrl);
|
||||||
|
const [encodedLbryURL, setEncodedLbryURL]: [string, any] = React.useState(
|
||||||
|
generateEncodedLbryURL(OPEN_URL, lbryWebUrl, includeStartTime, startTime)
|
||||||
|
);
|
||||||
|
const [openDotLbryDotComUrl, setOpenDotLbryDotComUrl]: [string, any] = React.useState(
|
||||||
|
generateOpenDotLbryDotComUrl(
|
||||||
|
OPEN_URL,
|
||||||
|
lbryWebUrl,
|
||||||
|
canonicalUrl,
|
||||||
|
permanentUrl,
|
||||||
|
referralCode,
|
||||||
|
rewardsApproved,
|
||||||
|
includeStartTime,
|
||||||
|
startTime
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const downloadUrl = `${generateDownloadUrl(name, claimId)}`;
|
||||||
|
|
||||||
if (!claim) {
|
if (!claim) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { canonical_url: canonicalUrl, permanent_url: permanentUrl, name, claim_id: claimId } = claim;
|
|
||||||
const isChannel = claim.value_type === 'channel';
|
|
||||||
const rewardsApproved = user && user.is_reward_approved;
|
|
||||||
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 referralParam: string = referralCode && rewardsApproved ? `?r=${referralCode}` : '';
|
|
||||||
const openDotLbryDotComUrl: string = `${OPEN_URL}${lbryWebUrl}${referralParam}`;
|
|
||||||
const downloadUrl = `${generateDownloadUrl(name, claimId)}`;
|
|
||||||
|
|
||||||
function handleWebShareClick() {
|
function handleWebShareClick() {
|
||||||
if (navigator.share) {
|
if (navigator.share) {
|
||||||
navigator.share({
|
navigator.share({
|
||||||
|
@ -48,9 +78,54 @@ function SocialShare(props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleTimeCheckboxChange(checked) {
|
||||||
|
setincludeStartTime(checked);
|
||||||
|
updateUrls(checked, startTimeSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTimeChange(value) {
|
||||||
|
setStartTime(value);
|
||||||
|
const startSeconds = hmsToSeconds(value);
|
||||||
|
setStartTimeSeconds(startSeconds);
|
||||||
|
updateUrls(true, startSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUrls(includeStartTime, startTime) {
|
||||||
|
setOpenDotLbryDotComUrl(
|
||||||
|
generateOpenDotLbryDotComUrl(
|
||||||
|
OPEN_URL,
|
||||||
|
lbryWebUrl,
|
||||||
|
canonicalUrl,
|
||||||
|
permanentUrl,
|
||||||
|
referralCode,
|
||||||
|
rewardsApproved,
|
||||||
|
includeStartTime,
|
||||||
|
startTime
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
setEncodedLbryURL(generateEncodedLbryURL(OPEN_URL, lbryWebUrl, includeStartTime, startTime));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CopyableText label={__('LBRY Link')} copyable={openDotLbryDotComUrl} />
|
<CopyableText label={__('LBRY Link')} copyable={openDotLbryDotComUrl} />
|
||||||
|
<div className="section__start-at">
|
||||||
|
<FormField
|
||||||
|
type="checkbox"
|
||||||
|
name="share_start_at_checkbox"
|
||||||
|
onChange={() => handleTimeCheckboxChange(!includeStartTime)}
|
||||||
|
checked={includeStartTime}
|
||||||
|
label={__('Start at')}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
type="text"
|
||||||
|
name="share_start_at"
|
||||||
|
value={startTime}
|
||||||
|
disabled={!includeStartTime}
|
||||||
|
onChange={event => handleTimeChange(event.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="section__actions">
|
<div className="section__actions">
|
||||||
<Button
|
<Button
|
||||||
className="share"
|
className="share"
|
||||||
|
@ -120,7 +195,14 @@ function SocialShare(props: Props) {
|
||||||
<Button icon={ICONS.SHARE} button="primary" label={__('Share via...')} onClick={handleWebShareClick} />
|
<Button icon={ICONS.SHARE} button="primary" label={__('Share via...')} onClick={handleWebShareClick} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{showEmbed && <EmbedTextArea label={__('Embedded')} claim={claim} />}
|
{showEmbed && (
|
||||||
|
<EmbedTextArea
|
||||||
|
label={__('Embedded')}
|
||||||
|
claim={claim}
|
||||||
|
includeStartTime={includeStartTime}
|
||||||
|
startTime={startTimeSeconds}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{showExtra && (
|
{showExtra && (
|
||||||
<div className="section">
|
<div className="section">
|
||||||
<CopyableText label={__('LBRY URL')} copyable={`lbry://${lbryUrl}`} />
|
<CopyableText label={__('LBRY URL')} copyable={`lbry://${lbryUrl}`} />
|
||||||
|
|
|
@ -138,3 +138,15 @@
|
||||||
.section__actions--no-margin {
|
.section__actions--no-margin {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section__start-at {
|
||||||
|
display: flex;
|
||||||
|
margin-top: var(--spacing-large);
|
||||||
|
fieldset-section {
|
||||||
|
width: 6em;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.checkbox {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,8 +6,9 @@ function generateStreamUrl(claimName, claimId) {
|
||||||
return `${LBRY_TV_STREAMING_API}/content/claims/${claimName}/${claimId}/stream`;
|
return `${LBRY_TV_STREAMING_API}/content/claims/${claimName}/${claimId}/stream`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateEmbedUrl(claimName, claimId) {
|
function generateEmbedUrl(claimName, claimId, includeStartTime, startTime) {
|
||||||
return `${URL}/$/embed/${claimName}/${claimId}`;
|
const queryParam = includeStartTime ? `?t=${startTime}` : '';
|
||||||
|
return `${URL}/$/embed/${claimName}/${claimId}${queryParam}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDownloadUrl(claimName, claimId) {
|
function generateDownloadUrl(claimName, claimId) {
|
||||||
|
|
34
ui/util/time.js
Normal file
34
ui/util/time.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
export function secondsToHms(seconds: number) {
|
||||||
|
seconds = Math.floor(seconds);
|
||||||
|
var hours = Math.floor(seconds / 3600);
|
||||||
|
var minutes = Math.floor(seconds / 60) % 60;
|
||||||
|
var seconds = seconds % 60;
|
||||||
|
|
||||||
|
return [hours, minutes, seconds]
|
||||||
|
.map(v => (v < 10 ? '0' + v : v))
|
||||||
|
.filter((v, i) => v !== '00' || i > 0)
|
||||||
|
.join(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hmsToSeconds(str: string) {
|
||||||
|
let timeParts = str.split(':'),
|
||||||
|
seconds = 0,
|
||||||
|
multiplier = 1;
|
||||||
|
|
||||||
|
if (timeParts.length > 0) {
|
||||||
|
while (timeParts.length > 0) {
|
||||||
|
let nextPart = parseInt(timeParts.pop(), 10);
|
||||||
|
if (!Number.isInteger(nextPart)) {
|
||||||
|
nextPart = 0;
|
||||||
|
}
|
||||||
|
seconds += multiplier * nextPart;
|
||||||
|
multiplier *= 60;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seconds = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return seconds;
|
||||||
|
}
|
|
@ -70,3 +70,41 @@ exports.generateInitialUrl = hash => {
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.generateLbryUrl = (canonicalUrl, permanentUrl) => {
|
||||||
|
return canonicalUrl ? canonicalUrl.split('lbry://')[1] : permanentUrl.split('lbry://')[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.generateLbryWebUrl = lbryUrl => {
|
||||||
|
return lbryUrl.replace(/#/g, ':');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.generateEncodedLbryURL = (openUrl, lbryWebUrl, includeStartTime, startTime) => {
|
||||||
|
const queryParam = includeStartTime ? `?t=${startTime}` : '';
|
||||||
|
const encodedPart = encodeURIComponent(`${lbryWebUrl}${queryParam}`);
|
||||||
|
return `${openUrl}${encodedPart}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.generateOpenDotLbryDotComUrl = (
|
||||||
|
openUrl,
|
||||||
|
lbryWebUrl,
|
||||||
|
canonicalUrl,
|
||||||
|
permanentUrl,
|
||||||
|
referralCode,
|
||||||
|
rewardsApproved,
|
||||||
|
includeStartTime,
|
||||||
|
startTime
|
||||||
|
) => {
|
||||||
|
let urlParams = new URLSearchParams();
|
||||||
|
if (referralCode && rewardsApproved) {
|
||||||
|
urlParams.append('r', referralCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeStartTime) {
|
||||||
|
urlParams.append('t', startTime.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlParamsString = urlParams.toString();
|
||||||
|
const url = `${openUrl}${lbryWebUrl}` + (urlParamsString === '' ? '' : `?${urlParamsString}`);
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue